Организация системы объектов с менеджерами внешних компонентов?


13

Я разрабатываю игровой движок для многопользовательской 2D-стрелялки сверху вниз, которую я хочу разумно использовать в других стрелялках сверху вниз. В данный момент я думаю о том, как должна быть спроектирована какая-то система сущностей в ней. Сначала я подумал об этом:

У меня есть класс под названием EntityManager. Он должен реализовывать метод с именем Update, а другой - с именем Draw. Причина, по которой я разделяю логику и рендеринг, заключается в том, что тогда я могу опустить метод Draw при работе на автономном сервере.

EntityManager владеет списком объектов типа BaseEntity. Каждой сущности принадлежит список компонентов, таких как EntityModel (отображаемое представление сущности), EntityNetworkInterface и EntityPhysicalBody.

EntityManager также владеет списком менеджеров компонентов, таких как EntityRenderManager, EntityNetworkManager и EntityPhysicsManager. Каждый менеджер компонентов хранит ссылки на компоненты сущности. Существуют различные причины для перемещения этого кода из собственного класса сущности и сделать это коллективно. Например, для игры я использую внешнюю физическую библиотеку Box2D. В Box2D вы сначала добавляете тела и фигуры в мир (в данном случае принадлежащий EntityPhysicsManager) и добавляете колбэки обратного вызова (которые будут отправлены самому объекту сущности в моей системе). Затем вы запускаете функцию, которая имитирует все в системе. Мне трудно найти лучшее решение, чтобы сделать это, чем делать это в менеджере внешних компонентов, как это.

Создание сущности выполняется следующим образом: EntityManager реализует метод RegisterEntity (entityClass, factory), который регистрирует, как создать сущность этого класса. Он также реализует метод CreateEntity (entityClass), который возвращает объект типа BaseEntity.

Теперь возникает моя проблема: как ссылка на компонент будет зарегистрирована для менеджеров компонентов? Я понятия не имею, как бы я ссылался на менеджеров компонентов с завода / закрытия.


Я не знаю, может ли это быть гибридной системой, но звучит так, будто ваши «менеджеры» - это то, что я обычно слышал как «системы»; т.е. сущность является абстрактным идентификатором; Компонент - это пул данных; и то, что вы называете «менеджером», - это то, что обычно называют «Системой». Я правильно истолковываю словарный запас?
BRPocock


Взгляните на gamadu.com/artemis и посмотрите, ответят ли их методы на ваш вопрос.
Патрик Хьюз

1
Не существует единого способа проектирования системы сущностей, так как нет единого мнения по поводу ее определения. То, что описывает @BRPocock, а также то, что использует Артемида, более подробно описано в этом блоге: t-machine.org/index.php/category/entity-systems вместе с wiki: entity-systems.wikidot.com
user8363

Ответы:


6

Системы должны хранить пару ключ-значение от сущности к компоненту в некотором виде карты, словарного объекта или ассоциативного массива (в зависимости от используемого языка). Кроме того, когда вы создаете свой объект Entity, я не буду беспокоиться о его сохранении в менеджере, если вам не понадобится отменить его регистрацию в любой из систем. Сущность - это совокупность компонентов, но она не должна обрабатывать обновления компонента. Это должно быть обработано системами. Вместо этого рассматривайте вашу сущность как ключ, который сопоставлен со всеми компонентами, которые он содержит в системах, а также как коммуникационный концентратор, с помощью которого эти компоненты могут общаться друг с другом.

Большая часть моделей Entity-Component-System заключается в том, что вы можете легко реализовать возможность передачи сообщений от одного компонента к остальным компонентам объекта. Это позволяет компоненту общаться с другим компонентом, фактически не зная, кто этот компонент или как обрабатывать компонент, который он меняет. Вместо этого он передает сообщение и позволяет компоненту изменить себя (если он существует)

Например, система позиционирования не будет иметь много кода, только отслеживая объекты сущностей, сопоставленные с их компонентами позиции. Но когда позиция меняется, они могут отправить сообщение вовлеченному объекту, которое, в свою очередь, передается всем компонентам этого объекта. Позиция меняется по какой-либо причине? Система позиционирования отправляет объекту сообщение о том, что позиция изменилась, и где-то компонент визуализации изображения этого объекта получает это сообщение и обновляет, где он будет рисовать себя следующим.

И наоборот, Физическая Система должна знать, что делают все ее объекты; Он должен иметь возможность видеть все объекты мира, чтобы проверить столкновения. Когда происходит коллизия, он обновляет компонент направления сущности, посылая объекту своего рода «Сообщение об изменении направления» вместо прямой ссылки на компонент сущности. Это избавляет менеджера от необходимости знать, как изменить направление, используя сообщение вместо того, чтобы полагаться на определенный компонент, находящийся там (которого он может вообще не иметь), и в этом случае сообщение будет просто не слышать, а не сообщать об ошибке. происходящее, потому что ожидаемый объект отсутствовал).

Вы заметите огромное преимущество от этого, так как упомянули, что у вас есть сетевой интерфейс. Сетевой компонент будет прослушивать все поступающие сообщения, о которых все остальные должны знать. Любит сплетни. Затем, когда сетевая система обновляется, сетевые компоненты отправляют эти сообщения другим сетевым системам на других клиентских компьютерах, которые затем повторно отправляют эти сообщения всем другим компонентам для обновления позиций игрока и т. Д. Может потребоваться специальная логика, чтобы только определенные объекты могли отправлять сообщения по сети, но в этом вся прелесть Системы, вы можете просто контролировать ее, регистрируя в ней нужные вещи.

Короче говоря:

Сущность - это композиция Компонентов, которые могут получать сообщения. Сущность может получать сообщения, делегируя указанные сообщения всем своим компонентам для их обновления. (Сообщение об изменении позиции, направление изменения скорости и т. Д.) Это похоже на центральный почтовый ящик, в котором все компоненты могут слышать друг от друга, а не разговаривать напрямую друг с другом.

Компонент - это небольшая часть сущности, которая хранит некоторое состояние сущности. Они могут анализировать определенные сообщения и выбрасывать другие сообщения. Например, «Компонент направления» будет заботиться только о «сообщениях об изменении направления», но не о «сообщениях об изменении положения». Компоненты обновляют свое собственное состояние на основе сообщений, а затем обновляют состояния других компонентов, отправляя сообщения из своей Системы.

Система управляет всеми компонентами определенного типа и отвечает за обновление указанных компонентов в каждом кадре, а также за отправку сообщений от компонентов, которыми они управляют, к сущностям, к которым принадлежат компоненты.

Системы могут обновлять все свои компоненты параллельно и сохранять все сообщения по мере их поступления. Затем, когда завершится выполнение всех методов обновления систем, вы попросите каждую систему отправить свои сообщения в определенном порядке. Сначала возможно управление, затем физика, затем направление, положение, рендеринг и т. Д. Имеет значение, в каком порядке они отправляются, поскольку изменение направления физики должно всегда взвешивать изменение направления, основанное на контроле.

Надеюсь это поможет. Это чертовски шаблон дизайна, но это невероятно мощно, если все сделано правильно.


0

Я использую подобную систему в своем движке, и способ, которым я это сделал, заключается в том, что каждый объект содержит список компонентов. Из EntityManager я могу запросить каждую из сущностей и посмотреть, какие из них содержат данный компонент. Пример:

class Component
{
    private uint ID;
    // etc...
}

class Entity
{
    List<Component> Components;
    // etc...
    public bool Contains(Type type)
    {
        foreach(Component comp in Components)
        {
            if(typeof(comp) == type)
                return true;
        }
        return false;
    }
}

class EntityManager
{
    List<Entity> Entities;
    // etc...
    public List<Entity> GetEntitiesOfType(Type type)
    {
        List<Entity> results = new List<Entity>();
        foreach(Entity entity in Entities)
        {
            if(entity.Contains(type))
                results.Add(entity);
        }
        return results;
    }
}

Очевидно, что это не точный код (на самом деле вам нужна функция шаблона для проверки различных типов компонентов, а не для их использования typeof), но концепция есть. Затем вы можете взять эти результаты, сослаться на искомый компонент и зарегистрировать его на своем заводе. Это предотвращает любую прямую связь между Компонентами и их менеджерами.


3
Будьте бдительны: в тот момент, когда ваша сущность содержит данные, это объект, а не сущность ... Один теряет большинство парализующих (sic?) Преимуществ ECS в этой структуре. «Чистые» E / C / S-системы являются реляционными, а не объектно-ориентированными… Не то чтобы это обязательно «плохо» для некоторых случаев, но это, безусловно, «нарушение реляционной модели»
BRPocock

2
Я не уверен, что понимаю вас. Я понимаю (и, пожалуйста, поправьте меня, если я ошибаюсь), что базовая Entity-Component-System имеет класс Entity, в котором находятся компоненты и может иметь идентификатор, имя или какой-либо идентификатор. Я думаю, что у нас может быть неправильное понимание того, что я имею в виду под «типом» сущности. Когда я говорю Entity «тип», я имею в виду типы компонентов. То есть сущность является типом «Sprite», если она содержит компонент Sprite.
Майк Клак

1
В чистой системе сущностей / компонентов сущность обычно является атомарной: например typedef long long int Entity; Компонент - это запись (она может быть реализована как класс объекта или просто a struct), которая имеет ссылку на сущность, к которой она присоединена; и Система будет методом или совокупностью методов. Модель ECS не очень совместима с моделью ООП, хотя Компонент может быть (главным образом) Объектом только для данных, а Система - одноэлементным Объектом только для кода, состояние которого находится в компонентах ... хотя "гибридные" системы чаще, чем «чистые», они теряют многие из врожденных преимуществ.
BRPocock

2
@BRPocock - «чистые» системы сущностей. Я думаю, что сущность как объект совершенно в порядке, это не должен быть простой идентификатор. Одно - это сериализованное представление, другое - расположение объекта / концепции / объекта в памяти. Пока вы можете поддерживать управляемость данными, не следует привязываться к неидиоматическому коду только потому, что это «чистый» способ.
Рейн

1
@BRPocock - это справедливое предупреждение, но для систем сущностей, подобных "t-machine". Я понимаю почему, но это не единственные способы моделирования компонентов на основе компонентов. Актеры - интересная альтернатива. Я склонен ценить их больше, особенно за чисто логические сущности.
Рейн

0

1) Вашему методу Factory нужно передать ссылку на EntityManager, который его вызвал (я буду использовать C # в качестве примера):

delegate BaseEntity EntityFactory(EntityManager manager);

2) Пусть CreateEntity также получает идентификатор (например, строку, целое число, все зависит от вас) помимо класса / типа объекта и автоматически регистрирует созданный объект в Словаре, используя этот идентификатор в качестве ключа:

class EntityManager
{
    // Rest of class omitted

    BaseEntity CreateEntity(string id, Type entityClass)
    {
        BaseEntity entity = factories[entityClass](this);
        registry.Add(id, entity);
        return entity;
    }

    Dictionary<Id, BaseEntity> registry;
}

3) Добавьте Getter в EntityManager, чтобы получить любую сущность по ID:

class EntityManager
{
    // Rest of class omitted

    BaseEntity GetEntity(string id)
    {
        return registry[id];
    }
}

И это все, что вам нужно для ссылки на любой ComponentManager из вашего метода Factory. Например:

BaseEntity CreateSomeSortOfEntity(EntityManager manager)
{
    // Create and configure entity
    BaseEntity entity = new BaseEntity();
    RenderComponent renderComponent = new RenderComponent();
    entity.AddComponent(renderComponent);

    // Get a reference to the render manager and register component
    RenderEntityManager renderer = manager.GetEntity("RenderEntityManager") as RenderEntityManager;
    if(renderer != null)
        renderer.Register(renderComponent)

    return entity;
}

Помимо Id вы также можете использовать какое-то свойство Type (пользовательское перечисление или просто положиться на систему типов языка) и создать метод получения, который возвращает все BaseEntities определенного типа.


1
Не для того, чтобы быть педантичным, но опять же ... в чистой сущностной (реляционной) системе сущности не имеют типа, за исключением того, что им сообщается в силу их компонентов ...
BRPocock

@BRPocock: Не могли бы вы создать пример, который следует чистой добродетели?
Zolomon

1
@Raine Возможно, у меня нет непосредственного опыта с этим, но это то, что я прочитал. И есть оптимизации, которые вы можете реализовать, чтобы сократить время, затрачиваемое на поиск компонентов по идентификатору. Что касается согласованности кэша, я думаю, что это имеет смысл, поскольку вы храните данные одного и того же типа непрерывно в памяти, особенно когда ваши компоненты имеют легкие или простые свойства. Я читал, что одиночная ошибка кэша на PS3 может стоить столько же, сколько и тысяча инструкций процессора, и такой подход к непрерывному хранению данных подобного типа является очень распространенным методом оптимизации в современной разработке игр.
Дэвид Гувейя

2
В работе: «чистая» система объекта: идентификатор объекта , как правило , что - то вроде: typedef unsigned long long int EntityID;; в идеале каждая система может жить на отдельном процессоре или хосте и требовать выборки только тех компонентов, которые относятся к этой системе или активны в ней. При использовании объекта Entity может потребоваться создать один и тот же объект Entity на каждом хосте, что усложняет масштабирование. Чистая модель сущность-компонент-система разделяет обработку по узлам (процессам, процессорам или хостам) по системе, а не по сущности, как правило.
BRPocock

1
@DavidGouveia упомянул «оптимизации… поиск сущностей по идентификатору». На самом деле, (несколько) систем, которые я реализовал таким образом, как правило, этого не делают. Чаще всего выбирают Компоненты по некоторому шаблону, указывающему, что они представляют интерес для конкретной Системы, используя сущности (идентификаторы) только для межкомпонентных объединений.
BRPocock
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.