Прочитав несколько документов о сущности-компонентной системе, я решил реализовать свою. Пока у меня есть класс World, который содержит сущности и системный менеджер (системы), класс Entity, который содержит компоненты в виде std :: map, и несколько систем. Я держу сущности как std :: vector в Мире. Пока проблем нет. Что меня смущает, так это итерация сущностей, у меня не может быть кристально чистого разума, поэтому я все еще не могу реализовать эту часть. Должна ли каждая система иметь локальный список сущностей, в которых они заинтересованы? Или я должен просто выполнить итерацию по сущностям в классе World и создать вложенный цикл для итерации по системам и проверить, есть ли у сущности компоненты, которые интересуют систему? Я имею в виду :
for (entity x : listofentities) {
for (system y : listofsystems) {
if ((x.componentBitmask & y.bitmask) == y.bitmask)
y.update(x, deltatime)
}
}
но я думаю, что система битовых масок будет блокировать гибкость в случае внедрения языка сценариев. Или наличие локальных списков для каждой системы увеличит использование памяти для классов. Я ужасно смущен.
источник
Ответы:
Это традиционный компромисс пространства-времени .
Хотя итерация по всем сущностям и проверка их сигнатур прямолинейны для кода, она может стать неэффективной по мере роста числа ваших систем - представьте себе специализированную систему (пусть она будет входной), которая ищет свой, вероятно, единственный объект интереса среди тысяч не связанных между собой сущностей ,
Тем не менее, этот подход все еще может быть достаточно хорошим в зависимости от ваших целей.
Хотя, если вы беспокоитесь о скорости, есть, конечно, другие решения для рассмотрения.
Точно. Это стандартный подход, который должен дать вам достойную производительность и достаточно прост в реализации. Накладные расходы памяти, на мой взгляд, ничтожны - мы говорим о хранении указателей.
Теперь, как поддерживать эти «списки интересов», может быть не так очевидно. Что касается контейнера данных, то
std::vector<entity*> targets
внутри класса системы вполне достаточно. Теперь то, что я делаю, это:Всякий раз, когда я добавляю компонент к объекту:
переберите все мировые системы, и если есть система, чья подпись не соответствует текущей подписи сущности и действительно совпадает с новой подписью, становится очевидным, что мы должны отсылать туда указатель на нашу сущность.
Удаление сущности полностью аналогично, с той разницей, которую мы удаляем, если система соответствует нашей текущей подписи (что означает, что сущность была там), и не совпадает с новой подписью (что означает, что сущность больше не должна быть там). ).
Теперь вы можете рассмотреть возможность использования std :: list, потому что удаление из вектора - это O (n), не говоря уже о том, что вам придется сдвигать большой кусок данных каждый раз, когда вы удаляете из середины. На самом деле, вам не нужно - поскольку нам не нужен порядок обработки на этом уровне, мы можем просто вызвать std :: remove и согласиться с тем фактом, что при каждом удалении нам нужно только выполнить O (n) поиск нашего подлежащее удалению лицо.
std :: list даст вам O (1) удалить, но с другой стороны у вас есть немного дополнительной памяти. Также помните, что большую часть времени вы будете обрабатывать объекты, а не удалять их - и это, безусловно, выполняется быстрее с использованием std :: vector.
Если вы очень критичны к производительности, вы можете рассмотреть даже другой шаблон доступа к данным , но в любом случае вы поддерживаете какие-то «списки интересов». Однако помните, что если вы сохраняете свой API-интерфейс Entity System достаточно абстрагированным, не должно возникнуть проблем с улучшением методов обработки сущностей систем, если из-за них снижается частота кадров - поэтому пока выберите метод, который проще всего для кода - только затем профиль и улучшить, если это необходимо.
источник
Есть подход, который стоит рассмотреть, когда каждая система владеет компонентами, связанными с ней, а сущности ссылаются только на них. По сути, ваш (упрощенный)
Entity
класс выглядит так:Когда вы говорите, что
RigidBody
компонент подключен кEntity
, вы запрашиваете его у своейPhysics
системы. Система создает компонент и позволяет объекту сохранять указатель на него. Ваша система выглядит так:Теперь это может показаться немного противоречивым на первый взгляд, но преимущество заключается в том, как системы сущностей компонентов обновляют свое состояние. Часто вы будете перебирать свои системы и запрашивать обновление соответствующих компонентов.
Сила того, что все компоненты, принадлежащие системе, находятся в смежной памяти, состоит в том, что когда ваша система выполняет итерацию по каждому компоненту и обновляет его, она в основном должна
Он не должен перебирать все объекты, которые потенциально не имеют компонента, который необходимо обновить, и он также имеет потенциал для очень хорошей производительности кэша, поскольку все компоненты будут храниться непрерывно. Это одно, если не самое большое преимущество этого метода. Вы часто будете иметь сотни и тысячи компонентов в одно и то же время, с таким же успехом можете попробовать и быть максимально производительными.
В этот момент ваши
World
единственные циклы проходят по системам и вызываютupdate
их без необходимости итерации сущностей. Это (imho) лучший дизайн, потому что тогда обязанности систем намного яснее.Конечно, существует множество таких дизайнов, поэтому вы должны тщательно оценить потребности вашей игры и выбрать наиболее подходящую, но, как мы видим здесь, иногда мелкие детали дизайна могут иметь значение.
источник
На мой взгляд, хорошей архитектурой является создание слоя компонентов в объектах и разделение управления каждой системой на этом уровне компонентов. Например, логическая система имеет некоторые логические компоненты, которые влияют на их сущность, и хранят общие атрибуты, которые являются общими для всех компонентов в сущности.
После этого, если вы хотите управлять объектами каждой системы в разных точках или в определенном порядке, лучше создать список активных компонентов в каждой системе. Все списки указателей, которые вы можете создавать и управлять в системах, содержат менее одного загруженного ресурса.
источник