Я разрабатываю игровой движок для многопользовательской 2D-стрелялки сверху вниз, которую я хочу разумно использовать в других стрелялках сверху вниз. В данный момент я думаю о том, как должна быть спроектирована какая-то система сущностей в ней. Сначала я подумал об этом:
У меня есть класс под названием EntityManager. Он должен реализовывать метод с именем Update, а другой - с именем Draw. Причина, по которой я разделяю логику и рендеринг, заключается в том, что тогда я могу опустить метод Draw при работе на автономном сервере.
EntityManager владеет списком объектов типа BaseEntity. Каждой сущности принадлежит список компонентов, таких как EntityModel (отображаемое представление сущности), EntityNetworkInterface и EntityPhysicalBody.
EntityManager также владеет списком менеджеров компонентов, таких как EntityRenderManager, EntityNetworkManager и EntityPhysicsManager. Каждый менеджер компонентов хранит ссылки на компоненты сущности. Существуют различные причины для перемещения этого кода из собственного класса сущности и сделать это коллективно. Например, для игры я использую внешнюю физическую библиотеку Box2D. В Box2D вы сначала добавляете тела и фигуры в мир (в данном случае принадлежащий EntityPhysicsManager) и добавляете колбэки обратного вызова (которые будут отправлены самому объекту сущности в моей системе). Затем вы запускаете функцию, которая имитирует все в системе. Мне трудно найти лучшее решение, чтобы сделать это, чем делать это в менеджере внешних компонентов, как это.
Создание сущности выполняется следующим образом: EntityManager реализует метод RegisterEntity (entityClass, factory), который регистрирует, как создать сущность этого класса. Он также реализует метод CreateEntity (entityClass), который возвращает объект типа BaseEntity.
Теперь возникает моя проблема: как ссылка на компонент будет зарегистрирована для менеджеров компонентов? Я понятия не имею, как бы я ссылался на менеджеров компонентов с завода / закрытия.
источник
Ответы:
Системы должны хранить пару ключ-значение от сущности к компоненту в некотором виде карты, словарного объекта или ассоциативного массива (в зависимости от используемого языка). Кроме того, когда вы создаете свой объект Entity, я не буду беспокоиться о его сохранении в менеджере, если вам не понадобится отменить его регистрацию в любой из систем. Сущность - это совокупность компонентов, но она не должна обрабатывать обновления компонента. Это должно быть обработано системами. Вместо этого рассматривайте вашу сущность как ключ, который сопоставлен со всеми компонентами, которые он содержит в системах, а также как коммуникационный концентратор, с помощью которого эти компоненты могут общаться друг с другом.
Большая часть моделей Entity-Component-System заключается в том, что вы можете легко реализовать возможность передачи сообщений от одного компонента к остальным компонентам объекта. Это позволяет компоненту общаться с другим компонентом, фактически не зная, кто этот компонент или как обрабатывать компонент, который он меняет. Вместо этого он передает сообщение и позволяет компоненту изменить себя (если он существует)
Например, система позиционирования не будет иметь много кода, только отслеживая объекты сущностей, сопоставленные с их компонентами позиции. Но когда позиция меняется, они могут отправить сообщение вовлеченному объекту, которое, в свою очередь, передается всем компонентам этого объекта. Позиция меняется по какой-либо причине? Система позиционирования отправляет объекту сообщение о том, что позиция изменилась, и где-то компонент визуализации изображения этого объекта получает это сообщение и обновляет, где он будет рисовать себя следующим.
И наоборот, Физическая Система должна знать, что делают все ее объекты; Он должен иметь возможность видеть все объекты мира, чтобы проверить столкновения. Когда происходит коллизия, он обновляет компонент направления сущности, посылая объекту своего рода «Сообщение об изменении направления» вместо прямой ссылки на компонент сущности. Это избавляет менеджера от необходимости знать, как изменить направление, используя сообщение вместо того, чтобы полагаться на определенный компонент, находящийся там (которого он может вообще не иметь), и в этом случае сообщение будет просто не слышать, а не сообщать об ошибке. происходящее, потому что ожидаемый объект отсутствовал).
Вы заметите огромное преимущество от этого, так как упомянули, что у вас есть сетевой интерфейс. Сетевой компонент будет прослушивать все поступающие сообщения, о которых все остальные должны знать. Любит сплетни. Затем, когда сетевая система обновляется, сетевые компоненты отправляют эти сообщения другим сетевым системам на других клиентских компьютерах, которые затем повторно отправляют эти сообщения всем другим компонентам для обновления позиций игрока и т. Д. Может потребоваться специальная логика, чтобы только определенные объекты могли отправлять сообщения по сети, но в этом вся прелесть Системы, вы можете просто контролировать ее, регистрируя в ней нужные вещи.
Короче говоря:
Сущность - это композиция Компонентов, которые могут получать сообщения. Сущность может получать сообщения, делегируя указанные сообщения всем своим компонентам для их обновления. (Сообщение об изменении позиции, направление изменения скорости и т. Д.) Это похоже на центральный почтовый ящик, в котором все компоненты могут слышать друг от друга, а не разговаривать напрямую друг с другом.
Компонент - это небольшая часть сущности, которая хранит некоторое состояние сущности. Они могут анализировать определенные сообщения и выбрасывать другие сообщения. Например, «Компонент направления» будет заботиться только о «сообщениях об изменении направления», но не о «сообщениях об изменении положения». Компоненты обновляют свое собственное состояние на основе сообщений, а затем обновляют состояния других компонентов, отправляя сообщения из своей Системы.
Система управляет всеми компонентами определенного типа и отвечает за обновление указанных компонентов в каждом кадре, а также за отправку сообщений от компонентов, которыми они управляют, к сущностям, к которым принадлежат компоненты.
Системы могут обновлять все свои компоненты параллельно и сохранять все сообщения по мере их поступления. Затем, когда завершится выполнение всех методов обновления систем, вы попросите каждую систему отправить свои сообщения в определенном порядке. Сначала возможно управление, затем физика, затем направление, положение, рендеринг и т. Д. Имеет значение, в каком порядке они отправляются, поскольку изменение направления физики должно всегда взвешивать изменение направления, основанное на контроле.
Надеюсь это поможет. Это чертовски шаблон дизайна, но это невероятно мощно, если все сделано правильно.
источник
Я использую подобную систему в своем движке, и способ, которым я это сделал, заключается в том, что каждый объект содержит список компонентов. Из EntityManager я могу запросить каждую из сущностей и посмотреть, какие из них содержат данный компонент. Пример:
Очевидно, что это не точный код (на самом деле вам нужна функция шаблона для проверки различных типов компонентов, а не для их использования
typeof
), но концепция есть. Затем вы можете взять эти результаты, сослаться на искомый компонент и зарегистрировать его на своем заводе. Это предотвращает любую прямую связь между Компонентами и их менеджерами.источник
typedef long long int Entity
; Компонент - это запись (она может быть реализована как класс объекта или просто astruct
), которая имеет ссылку на сущность, к которой она присоединена; и Система будет методом или совокупностью методов. Модель ECS не очень совместима с моделью ООП, хотя Компонент может быть (главным образом) Объектом только для данных, а Система - одноэлементным Объектом только для кода, состояние которого находится в компонентах ... хотя "гибридные" системы чаще, чем «чистые», они теряют многие из врожденных преимуществ.1) Вашему методу Factory нужно передать ссылку на EntityManager, который его вызвал (я буду использовать C # в качестве примера):
2) Пусть CreateEntity также получает идентификатор (например, строку, целое число, все зависит от вас) помимо класса / типа объекта и автоматически регистрирует созданный объект в Словаре, используя этот идентификатор в качестве ключа:
3) Добавьте Getter в EntityManager, чтобы получить любую сущность по ID:
И это все, что вам нужно для ссылки на любой ComponentManager из вашего метода Factory. Например:
Помимо Id вы также можете использовать какое-то свойство Type (пользовательское перечисление или просто положиться на систему типов языка) и создать метод получения, который возвращает все BaseEntities определенного типа.
источник
typedef unsigned long long int EntityID;
; в идеале каждая система может жить на отдельном процессоре или хосте и требовать выборки только тех компонентов, которые относятся к этой системе или активны в ней. При использовании объекта Entity может потребоваться создать один и тот же объект Entity на каждом хосте, что усложняет масштабирование. Чистая модель сущность-компонент-система разделяет обработку по узлам (процессам, процессорам или хостам) по системе, а не по сущности, как правило.