Вот аналогия: у меня есть организм, состоящий из клеток, которые могут быть далее составлены из смеси привязанностей.
В настоящее время у меня есть своего рода цепочка событий между дочерними/родительскими элементами для обработки присоединения и отсоединения компонентов (что может повлиять на что-либо в цепочке), которая вообще не включает ecs, они являются функциями в сущностях.
Теперь я уже использовал компоненты событий (для событий мыши на объектах). Если бы я хотел, чтобы система была чистой, должен ли я создавать компонент присоединения, когда я прикрепляю компоненты и т. д.? Даже тогда, как я могу получить всех необходимых получателей для системы, которая потребляет компонент? Стоит ли вообще обрабатывать его таким образом вместо цепочки функций? Есть ли способ лучше?
Отказ от ответственности: я не уверен, что правильно понял ваш вопрос. Если это не так, прошу прощения за слухи.
Для управления иерархиями в ECS можно использовать специальный компонент, аналогичный следующему:
struct relationship {
entity_type first{entity_null};
entity_type prev{entity_null};
entity_type next{entity_null};
entity_type parent{entity_null};
// ... other data members ...
};
Где entity_type
— это тип, который вы используете для идентификаторов своих объектов, а entity_null
— это ваш способ сказать — это еще не установлено. Эти две вещи в основном зависят от фактической реализации. Например, в моем собственном (EnTT
) существует entt::null
, который является своего рода нулевая сущность для использования в таких случаях, как этот.
Давайте теперь рассмотрим общий узел в вашей иерархии:
* parent
— это идентификатор объекта родительского узла, так что вы можете легко пройти по дереву (иерархии) от листа к корню.
* first
— идентификатор сущности первых дочерних элементов, то есть список листьев или внутренних узлов поддерева, корневым узлом которого является текущий идентификатор.
* prev
и next
— идентификаторы сущностей братьев и сестер для текущего узла.
Когда вы хотите посещать всех дочерних элементов узла, вы начинаете с объекта first
(его первого дочернего элемента) и повторяете их по одному, посещая next
, пока он не станет нулевым. Если вы хотите вернуться от узла к его родителю, вы можете просто использовать parent
.
плюс этого решения заключается в том, что списки дочерних элементов неявно определяются с точки зрения компонентов, и вам не нужно использовать для них std::vector
или подобное. Следовательно, у вас нет динамически выделяемой памяти в ваших компонентах для создания иерархии.
Если вам интересно, я также написал сообщение на эту тему. Вы можете прочитать его, если хотите больше подробностей.