Система сущностей и компонентов в Hytale: техническое погружение
Всем привет! Сегодня мы приоткроем завесу над технологиями, лежащими в основе движка Hytale. Особое внимание уделим одному из недавно анонсированных фреймворков — Flecs. Это лёгкая и мощная система сущностей и компонентов (Entity Component System, ECS).

Некоторое время назад мы рассказывали о решении перезапустить движок Hytale. Мы перешли с Java-сервера и C#-клиента на разработку обеих частей на C++. Причин для этого было много: мы хотели обеспечить выход игры на нескольких платформах, улучшить производительность на устройствах с низкими характеристиками и создать надёжное ядро движка, которое позволит поддерживать и обновлять игру в будущем.
ECS — один из многих инструментов, помогающих нам достичь этих целей. В этой статье мы расскажем, почему выбрали Flecs в качестве основы ECS для нового движка Hytale и как именно он помогает нам решать поставленные задачи.
Но прежде чем углубиться в Flecs, давайте разберёмся с самим паттерном ECS — системой сущностей и компонентов (Entity Component System).
ОТ ОДНОЙ АББРЕВИАТУРЫ К ДРУГОЙ
ECS — не особо новая концепция, и в разработке игр она уже не так редка, как несколько лет назад. Тем не менее при первом знакомстве она может показаться сложной и непривычной. Чтобы правильно о ней рассказать, нужно поместить концепцию ECS в контекст разработки игр.
Большая часть традиционной разработки игр опирается на проверенную временем модель объектно-ориентированного программирования (ООП, OOP) или на архитектуры «сущность-компонент» (Entity-Component, или Actor-Component). Объектно-ориентированное программирование широко распространено в разработке ПО в целом. Оно разбивает задачи на знакомые структуры, которые можно представить как объекты.
Например, у вас может быть общий тип объекта «Персонаж» (Character), содержащий игровую логику, общую для всех персонажей. От него наследуются или специализируются объекты «Игрок» (Player), различные NPC и любые другие типы, которые можно считать персонажами.

Это дерево может стать очень широким.
Архитектура «сущность-компонент» (Entity-Component) приближает нас к ECS и является основной архитектурой многих популярных игровых движков, таких как Unreal и Unity. В этой парадигме у нас есть сущности (Entities) — отдельные единицы вроде «игрока», «NPC», «стула» — и компоненты (Components) — комбинация данных и функциональности, которую можно прикрепить к этим сущностям. Каждая сущность состоит из набора компонентов.
Если вы знакомы с ООП, парадигма «сущность-компонент» активно использует принцип «композиция вместо наследования» (Composition over Inheritance). Например: у игрока есть позиция в мире, способность считывать ввод с контроллера и инвентарь. У NPC тоже есть позиция, может быть инвентарь и какая-то поведенческая логика. А у нашего стула, увы, есть только позиция.
Пока вы не добавите ему поведенческую логику, и он не станет существом. [Дисклеймер: это для демонстрации!]
Сразу становится понятно, насколько такая структура расширяет возможности для моддинга. Она не только обеспечивает высокую управляемость через данные, где простое изменение компонентов NPC или объекта приводит к заметно другому поведению, но и теоретически позволяет создавать совершенно новую функциональность без необходимости изменять существующий код. С помощью скриптового языка можно создать собственный компонент и прикрепить его к нужным сущностям. Открывается множество возможностей.

Сущность можно составить множеством разных способов!
Но это ещё не ECS. Система сущностей и компонентов (Entity Component System, ECS) развивает эти концепции дальше. Если в модели «сущность-компонент» функциональность (методы и функции) находится внутри самого компонента, то ECS отделяет эту функциональность от данных и состояния, которые она обрабатывает.
Вместо того чтобы каждый компонент имел собственную логику обновления, у нас есть системы (Systems), которые сопоставляют сущности с определёнными наборами компонентов и воздействуют на них. Это означает, что с ECS мы по-прежнему можем составлять сущности из разных компонентов, но разделение приводит к архитектуре данных и логики, которая значительно эффективнее для аппаратного обеспечения и, следовательно, более производительна.
Многие детали того, как ECS достигает этих преимуществ в производительности, весьма технические. Достаточно сказать, что это включает использование преимуществ архитектуры CPU, структурирование данных в плотно упакованном виде для выгоды от локальности в паттернах доступа и использование этих паттернов для максимально возможной параллелизации логики.

Системы могут сопоставлять любые комбинации компонентов.
ECS В СТАРОМ ДВИЖКЕ
Мы знали, что хотим перейти на архитектуру ECS ещё во время разработки старого движка. Причины — прирост производительности и масштабируемости, а также естественное соответствие нашему подходу к конструированию и настройке игровых сущностей и акторов через данные. В результате мы разработали собственную реализацию концепции на Java и начали интегрировать её в старый сервер. На тот момент у нас не было эквивалента для C#-клиента, поэтому наша реализация работала только на сервере.
Часть этой работы включала рефакторинг существующей логики под паттерн ECS параллельно с разработкой новой функциональности. За это время мы извлекли много уроков. Главный из них: реализация надёжного и производительного ECS-фреймворка с нуля — невероятно сложная и трудоёмкая задача.
Существует множество разновидностей ECS, каждая со своими преимуществами и недостатками, но все требуют глубокого понимания и технической специализации для качественной реализации. Java также не всегда является самым производительным языком программирования, и мы шли на многие компромиссы и принимали проектные решения из-за её особенностей.
Даже тогда наша начальная реализация ECS давала заметные преимущества в производительности, а также новый подход к архитектуре систем, воплощающий принципы дизайна, управляемого данными (Data-Driven Design), которых мы хотели достичь. При правильной реализации мы могли бы упростить моддерам предоставление данных, влияющих на поведение игры, практически без технических знаний.
НАДЁЖНАЯ ОСНОВА ДЛЯ НОВОГО ДВИЖКА
Когда мы перезапустили движок, мы знали, что хотим продолжить использовать ECS, но также распространить его и на клиент, чтобы получить преимущества во всех возможных аспектах. Мы также знали, что переход на C++ означает возможность найти другие фреймворки — такие, которые не нужно создавать и поддерживать самим, с передовыми функциями, раздвигающими границы парадигмы.
Оценив все доступные варианты, мы остановились на Flecs — высокоразвитом ECS-фреймворке, написанном и поддерживаемом экспертом по ECS: Sander Mertens.
«Из коробки» он даёт нам доступ к множеству функций, характерных для большинства реализаций ECS, а также отличную производительность и кроссплатформенную совместимость. Будучи полностью написанным на C с API для C++, он значительно быстрее любой реализации на C# или Java, позволяя нам в полной мере использовать его умную реализацию параллелизации и многопоточности.
Ещё одно очевидное преимущество — нам не нужно поддерживать его самим. Flecs проверен в боевых условиях, регулярно получает обновления и исправления ошибок, а благодаря комплексному набору тестов мы можем быть относительно уверены в его стабильности.
Но, пожалуй, самым привлекательным аспектом стал широкий набор функций, выходящих за рамки традиционного ECS-фреймворка и обеспечивающих гибкость, которая выводит ECS на новый уровень.
Один из примеров — концепция «связей» (Relationships). Как и компоненты, связи — это данные, которые можно прикрепить к сущности, но эти данные используются для соединения одной сущности с другой. Хороший пример — связь «родитель-потомок» (Parent-Child), где у сущности игрока может быть сущность камеры в качестве потомка, которая следует за ним. Другой пример может быть ещё более буквальным: Сущность A Нравится (Likes) Сущности B. Используя эту структуру, мы можем легко выполнять запросы вроде «найди мне все сущности, которым Нравится Сущность B» или «найди мне все сущности, которые Нравятся Сущности B».
Во многих отношениях ECS похожа на базу данных, и Flecs в полной мере использует этот факт. Лежащий в основе движок правил (Rule Engine) — мощный инструмент, поддерживающий запросы к данным различными способами: от простого сопоставления для систем (например, система, обновляющая рост урожая на основе набора прикреплённых компонентов) до сложных поисков для игровой логики или отладки (например, найди мне всех NPC с мечами, агрессивных к игроку).
Кроме того, механизм совместного использования компонентов позволяет создать базовый тип актора, например «Персонаж» (Character), и сказать, что NPC или Игрок — это Персонаж. Это даёт нам доступ к наследованию в стиле ООП, но построенному с учётом оптимизаций ECS.
Иногда некоторые из этих функций сложно осмыслить — как часто бывает при изучении совершенно новой архитектуры. Тем не менее, будучи понятыми и освоенными, они предоставляют чрезвычайно мощные инструменты разработки игр.
БЫСТРЕЕ ДУМАТЬ
В завершение рассмотрим один такой пример. Ранее мы представляли улучшение для NPC в Hytale под названием Оценщик боевых действий (Combat Action Evaluator). Это фреймворк, разработанный для того, чтобы NPC принимали более умные и «нечёткие» решения о том, какую атаку использовать и на какую цель, основываясь на множестве настраиваемых входных данных.
Хотя изначально он был реализован в старом движке, с самого начала он проектировался как управляемый данными: каждый отдельный входной параметр прикреплялся подобно компонентам в ECS.
В старом движке он отлично справлялся со своей задачей и обеспечивал боевых NPC, которых порой можно было принять за живых игроков. Однако приходилось накладывать ограничения из-за потенциального влияния на общую производительность игры. Ведь позволить NPC принимать «нечёткие» решения на основе огромного количества входных данных в среде ООП означает значительную вычислительную нагрузку — с которой наш Java-движок до внедрения ECS не справлялся.
Поэтому мы запускали оценщик боевых действий только через нерегулярные интервалы. Это позволяло избежать потенциального замедления сервера при большом количестве NPC в активном бою, но также означало, что они принимали решения медленнее — достаточно медленно, чтобы игрок это замечал.
С Flecs наши возможности геймдизайна больше не так ограничены соображениями производительности. Переработав внутренний фреймворк по паттернам ECS и активно используя функции Flecs, мы получаем эквивалент, которому больше не нужно обрабатывать эти данные столь неэффективным образом.
Вместо этого мы можем умно группировать все запросы, проверяющие определённую информацию, и параллелизировать их. Это приводит к значительно более быстрой обработке и устраняет необходимость в искусственных ограничениях частоты оценки. Там, где в старом движке мы последовательно выполняли проверки (приоритизируя дорогие, чтобы выйти раньше при неудаче!), теперь всё это может происходить одновременно, а движок запросов Flecs берёт на себя тяжёлую работу.
По сути, это означает, что NPC могут думать быстрее — реагируя на изменения в окружении и обстановке гораздо отзывчивее, чем когда-либо могли в старом движке.
В БУДУЩЕЕ
В конечном счёте это лишь поверхностный взгляд на возможности Flecs и ECS. Многие другие части движка предоставляют интересные возможности для оптимизации вокруг ECS — от базы данных ассетов до поэтапной генерации мира. По мере развития и Flecs, и движка Hytale мы ожидаем только роста возможностей.
Поделиться
Обсуждение
Обсудите эту публикацию с другими участниками сообщества:
Похожие публикации

Системные требования Hytale
01 декабря 2025
HYTALE СПАСЕНА!
17 ноября 2025
Стратегия и текущее состояние моддинга в Hytale
20 ноября 2025
Четыре новых трека из саундтрека Hytale
27 ноября 2019
Новости разработки Hytale за июль 2019 года
24 июля 2019
Галерея фан-арта Hytale: выпуск 4
24 сентября 2020
Растущие ожидания, новые вызовы и дальнейшие планы: обновление от команды Hytale
03 мая 2019