Это сложный вопрос. Я просто попытаюсь ответить на некоторые вопросы, основываясь на моем конкретном опыте (YMMV):
Компоненты должны иметь доступ к данным других компонентов. Например, метод рисования компонента рендеринга должен получить доступ к позиции компонента преобразования. Это создает зависимости в коде.
Не стоит недооценивать количество и сложность (не степень) связи / зависимости здесь. Вы могли бы взглянуть на разницу между этим (и эта диаграмма уже смехотворно упрощена до игрушечных уровней, и в реальном примере будут интерфейсы между ними, чтобы ослабить связь):
... и это:
... или это:
Компоненты могут быть полиморфными, что дополнительно вносит некоторую сложность. Например, может быть компонент рендеринга спрайта, который переопределяет виртуальный метод отрисовки компонента рендеринга.
Так? Аналогичный (или буквальный) эквивалент виртуальной и виртуальной таблиц может быть вызван через систему, а не через объект, скрывающий его основное состояние / данные. Полиморфизм все еще очень практичен и выполним с «чистой» реализацией ECS, когда аналогичный vtable или указатель (и) функции превращаются в своего рода «данные» для вызова системой.
Поскольку полиморфное поведение (например, для рендеринга) должно быть где-то реализовано, оно просто передается на аутсорсинг в системы. (например, система рендеринга спрайтов создает узел рендеринга спрайтов, который наследует узел рендеринга и добавляет его в механизм рендеринга)
Так? Я надеюсь, что это не сарказм (это не мое намерение, хотя меня часто в этом обвиняют, но я хотел бы лучше передавать эмоции с помощью текста), но полиморфное поведение "аутсорсинга" в этом случае не обязательно приводит к дополнительному стоимость для производительности.
Связи между системами может быть трудно избежать. Например, для системы столкновений может потребоваться ограничительная рамка, которая рассчитывается на основе любого конкретного компонента визуализации.
Этот пример кажется мне особенно странным. Я не знаю, почему рендерер будет выводить данные обратно на сцену (я обычно рассматриваю рендереры только для чтения в этом контексте) или для рендерера выяснять AABB вместо какой-либо другой системы, чтобы сделать это как для рендерера, так и для столкновение / физика (возможно, я зациклился на названии «компонента рендеринга»). И все же я не хочу слишком зацикливаться на этом примере, поскольку понимаю, что это не то, что вы пытаетесь сделать. Тем не менее, обмен данными между системами (даже в косвенной форме чтения / записи в центральную базу данных ECS с системами, зависящими скорее непосредственно от преобразований, сделанных другими) не должен быть частым, если вообще необходим. Который'
Это может привести к возникновению проблем, если не определен порядок вызова функций обновления системы.
Это абсолютно должно быть определено. ECS не является конечным решением для изменения порядка оценки обработки системы для каждой возможной системы в кодовой базе и получения конечных пользователей одинаковых результатов, имеющих дело с кадрами и FPS. Это одна из вещей, при разработке ECS, которую я, по крайней мере, настоятельно рекомендую, следует предвосхитить несколько заранее (хотя с большим количеством прощающей передышки, чтобы потом передумать, при условии, что это не изменяет наиболее важные аспекты упорядочения системный вызов / оценка).
Однако пересчет всей карты тайлов в каждом кадре обходится дорого. Поэтому для отслеживания всех изменений, внесенных с целью их последующего обновления в системе, потребуется список. В способе ООП это может быть инкапсулировано компонентом карты тайлов. Например, метод SetTile () будет обновлять массив вершин при каждом его вызове.
Я не совсем понял это, за исключением того, что это проблема, ориентированная на данные. И нет никаких ошибок в представлении и хранении данных в ECS, включая запоминание, чтобы избежать таких ошибок производительности (самые большие из них с ECS, как правило, связаны с такими вещами, как системы, запрашивающие доступные экземпляры определенных типов компонентов, что является одним из наиболее сложные аспекты оптимизации обобщенного ECS). Тот факт, что логика и данные разделены в «чистом» ECS, не означает, что вам вдруг придется пересчитывать вещи, которые вы могли бы иначе кэшировать / запоминать в представлении ООП. Это спорный / нерелевантный вопрос, если я не приукрашиваю что-то очень важное.
С «чистым» ECS вы все еще можете хранить эти данные в компоненте карты тайлов. Единственное существенное отличие состоит в том, что логика для обновления этого массива вершин куда-то переместится в систему.
Вы даже можете опираться на ECS, чтобы упростить аннулирование и удаление этого кэша из сущности, если вы создадите отдельный компонент, например TileMapCache
. В тот момент, когда кеш желателен, но недоступен в объекте с TileMap
компонентом, вы можете его вычислить и добавить. Когда он признан недействительным или больше не нужен, вы можете удалить его через ECS без необходимости писать больше кода специально для такого аннулирования и удаления.
Зависимости между компонентами все еще существуют, хотя и скрыты в системах
В «чистом» представлении нет зависимости между компонентами (я не думаю, что было бы правильно сказать, что зависимости скрыты здесь системами). Данные не зависят от данных, так сказать. Логика зависит от логики. И «чистый» ECS имеет тенденцию продвигать логику, которая должна быть написана таким образом, чтобы она зависела от абсолютного минимального подмножества данных и логики (часто ни одной), необходимой для работы системы, что в отличие от многих альтернатив, которые часто поощряются в зависимости от гораздо больше функциональности, чем требуется для реальной задачи. Если вы используете чистое право ECS, первое, что вы должны оценить - это преимущества разделения, одновременно задавая вопросы обо всем, что вы когда-либо учили ценить в ООП, об инкапсуляции и, в частности, о сокрытии информации.
Под развязкой я подразумеваю, как мало информации требуется вашим системам для работы. Ваша система движения даже не должна знать о чем-то гораздо более сложном, таком как Particle
или Character
(разработчик системы не обязательно должен даже знать, что такие сущностные идеи даже существуют в системе). Просто нужно знать о минимальных данных, таких как компонент позиции, который может быть таким же простым, как несколько операций с плавающей точкой в структуре. Это даже меньше информации и меньше внешних зависимостей, чем то, что имеет обыкновение иметь чистый интерфейс IMotion
. Это в первую очередь из-за этого минимального знания, что каждая система требует для работы, что делает ECS часто столь прощающим, чтобы справляться с непредвиденными изменениями дизайна задним числом, не сталкиваясь с каскадными поломками интерфейса повсюду.
«Нечистый» подход, который вы предлагаете, несколько уменьшает эту выгоду, поскольку теперь ваша логика не ограничивается строго системами, где изменения не вызывают каскадных поломок. Логика теперь будет в некоторой степени централизована в компонентах, к которым обращаются многочисленные системы, которые теперь должны удовлетворять требованиям интерфейса всех различных систем, которые могут его использовать, и теперь каждая система должна знать (зависеть) больше информация, чем это строго необходимо для работы с этим компонентом.
Зависимости от данных
Одна из спорных вещей в ECS - это то, что он имеет тенденцию заменять то, что иначе могло бы быть зависимостью, на абстрактные интерфейсы только с необработанными данными, и это обычно считается менее желательной и более жесткой формой связи. Но в таких областях, как игры, где ECS может быть очень полезным, зачастую проще спроектировать представление данных заранее и сохранить его стабильность, чем спроектировать то, что вы можете делать с этими данными на каком-то центральном уровне системы. Это то, что я болезненно наблюдал даже среди опытных ветеранов в основах кода, которые используют больше чисто интерфейсного подхода в стиле COM с такими вещами, как IMotion
.
Разработчики продолжали находить причины для добавления, удаления или изменения функций в этом центральном интерфейсе, и каждое изменение было ужасным и дорогостоящим, потому что оно, как правило, ломало каждый отдельный класс, который внедрялся IMotion
вместе с каждым последующим местом в системе, которая использовалась IMotion
. Между тем, все время с таким количеством болезненных и каскадных изменений IMotion
все реализованные объекты просто хранили матрицу с плавающей точкой 4х4, а весь интерфейс был просто озабочен тем, как преобразовать и получить доступ к этим числам; представление данных было стабильным с самого начала, и можно было бы избежать большой боли, если бы этот централизованный интерфейс, столь подверженный изменениям с непредвиденными потребностями проектирования, вообще не существовал.
Все это может звучать почти так же отвратительно, как и глобальные переменные, но природа того, как ECS организует эти данные в компоненты, извлекаемые в явном виде по типам через системы, делает это так, в то время как компиляторы не могут навязать что-либо, например, сокрытие информации, места, которые обращаются и изменяются данные, как правило, являются очень явными и достаточно очевидными, чтобы все еще эффективно поддерживать инварианты и предсказывать, какого рода преобразования и побочные эффекты переходят из одной системы в другую (фактически способами, которые, возможно, могут быть проще и более предсказуемыми, чем ООП в определенных областях, учитывая, как система превращается в плоский трубопровод).
Наконец, я хочу задать вопрос о том, как бы я справлялся с анимацией в чистом ECS. В настоящее время я определил анимацию как функтор, который управляет сущностью, основываясь на некотором прогрессе между 0 и 1. У компонента анимации есть список аниматоров, у которого есть список анимаций. В своей функции обновления он применяет все анимации, которые в данный момент активны к объекту.
Мы все здесь прагматики. Даже в gamedev вы, вероятно, получите противоречивые идеи / ответы. Даже самая чистая ECS - это относительно новое явление, новаторская территория, для которой люди не обязательно формулируют самые сильные мнения о том, как ухаживать за кошками. Моя внутренняя реакция - система анимации, которая увеличивает прогресс анимации в анимированных компонентах для отображения системы рендеринга, но при этом игнорируется столько нюансов для конкретного приложения и контекста.
С ECS это не серебряная пуля, и у меня все еще есть склонность входить и добавлять новые системы, удалять некоторые, добавлять новые компоненты, изменять существующую систему, чтобы подобрать этот новый тип компонентов, и т. Д. Я не понимаю все правильно с первого раза все еще. Но разница в моем случае заключается в том, что я ничего не меняю, если не могу заранее предвидеть определенные потребности в дизайне. Я не получаю волнообразного эффекта от каскадных поломок, которые требуют, чтобы я разбирался повсюду и изменял так много кода, чтобы справиться с какой-то новой потребностью, которая возникает, и это довольно экономит время. Мне также кажется, что мне легче это делать, потому что когда я работаю с конкретной системой, мне не нужно знать / запоминать так много о чем-то еще, кроме соответствующих компонентов (которые являются просто данными), чтобы работать над ней.