Это продолжение этого вопроса, на который я ответил, но этот вопрос затрагивает гораздо более конкретную тему.
Этот ответ помог мне понять Entity Systems даже лучше, чем статья.
Я прочитал (да) статью о Entity Systems, и она сказала мне следующее:
Сущности - это всего лишь идентификатор и массив компонентов (в статьях говорится, что хранение сущностей в компонентах не является хорошим способом ведения дел, но не предоставляет альтернативы).
Компоненты - это фрагменты данных, которые указывают, что можно сделать с определенной сущностью.
Системы являются «методами», они выполняют манипуляции данными на объектах.
Это кажется действительно практичным во многих ситуациях, но часть о компонентах, являющихся просто классами данных, беспокоит меня. Например, как я могу реализовать свой класс Vector2D (Position) в Entity System?
Класс Vector2D содержит данные: координаты x и y, но он также имеет методы , которые имеют решающее значение для его полезности и отличают класс от всего двухэлементного массива. Пример метода: add()
, rotate(point, r, angle)
, substract()
, normalize()
, и все другие стандартный, полезные, и абсолютно необходимы методы , что позиции (которые являются экземплярами класса Vector2D) должны иметь.
Если бы компонент был просто держателем данных, у него не было бы этих методов!
Вероятно, может появиться одно из решений, заключающееся в том, чтобы внедрить их в системы, но это кажется очень нелогичным. Эти методы - вещи, которые я хочу выполнить сейчас , чтобы они были завершены и готовы к использованию. Я не хочу ждать, пока MovementSystem
кто-то прочитает дорогой набор сообщений, которые инструктируют его выполнить вычисление позиции сущности!
И в статье очень четко говорится, что только системы должны иметь какую-либо функциональность, и единственное объяснение этому, которое я мог найти, было «избегать ООП». Прежде всего, я не понимаю, почему я должен воздерживаться от использования методов в сущностях и компонентах. Затраты памяти практически одинаковы, и в сочетании с системами их очень легко реализовать и объединить интересными способами. Системы, например, могут предоставлять базовую логику только сущностям / компонентам, которые сами знают реализацию. Если вы спросите меня - это, по сути, получение положительных отзывов как от ES, так и от ООП, что, по мнению автора статьи, сделать невозможно, но мне кажется хорошей практикой.
Подумайте об этом таким образом; В игре есть много разных типов прорисовываемых объектов. Простые старые изображения, анимации ( update()
и getCurrentFrame()
т. Д.), Комбинации этих примитивных типов, и все они могут просто предоставить draw()
метод для системы рендеринга, который затем не должен заботиться о том, как реализован спрайт объекта, только об интерфейсе (ничья) и позиции. И тогда мне понадобится только система анимации, которая будет вызывать специфичные для анимации методы, которые не имеют ничего общего с рендерингом.
И еще одна вещь ... Есть ли действительно альтернатива массивам, когда речь идет о хранении компонентов? Я не вижу другого места для хранения компонентов, кроме массивов внутри класса Entity ...
Возможно, это лучший подход: хранить компоненты как простые свойства сущностей. Например, компонент позиции будет приклеен к entity.position
.
Только другой способ будет иметь какое - то странную справочную таблицу внутри системы, что ссылки на различные объекты. Но это кажется очень неэффективным и более сложным для разработки, чем просто хранение компонентов в объекте.
Ответы:
Я думаю, что совершенно нормально иметь простые методы для доступа, обновления или манипулирования данными в компонентах. Я думаю, что функциональность, которая должна оставаться вне компонентов, является логической функциональностью. Служебные функции просто отлично. Помните, что система сущностей и компонентов - это просто руководство, а не строгие правила, которым вы должны следовать. Не старайся следовать за ними. Если вы думаете, что имеет смысл сделать это одним способом, то сделайте это так :)
РЕДАКТИРОВАТЬ
Чтобы уточнить, ваша цель не избежать ООП . Это было бы довольно сложно в большинстве распространенных языков, используемых в наши дни. Вы пытаетесь минимизировать наследование , которое является важным аспектом ООП, но не обязательным. Вы хотите избавиться от Object-> MobileObject-> Creature-> Bipedal-> Human type Наследование.
Тем не менее, нормально иметь какое-то наследство! Вы имеете дело с языком, который сильно зависит от наследования, очень трудно не использовать его. Например, у вас может быть
Component
класс или интерфейс, который расширяют или реализуют все остальные ваши компоненты. То же самое касается вашегоSystem
класса. Это делает вещи намного проще. Я настоятельно рекомендую вам взглянуть на рамки Artemis . Это открытый исходный код, и у него есть несколько примеров проектов. Откройте эти вещи и посмотрите, как это работает.Для Артемиды сущности хранятся в массиве, просто. Однако их компоненты хранятся в массиве или массивах (отдельно от сущностей). Массив верхнего уровня группирует массив нижнего уровня по типу компонента. Таким образом, каждый тип компонента имеет свой собственный массив. Массив нижнего уровня индексируется по идентификатору объекта. (Сейчас я не уверен, если бы я сделал это таким образом, но это так, как здесь). Артемида повторно использует идентификаторы сущностей, поэтому максимальный идентификатор сущности не превышает вашего текущего числа сущностей, но вы все равно можете иметь разреженные массивы, если компонент не является часто используемым компонентом. Во всяком случае, я не буду разбирать это слишком много. Этот метод для хранения сущностей и их компонентов, кажется, работает. Я думаю, что это будет отличным первым шагом на пути внедрения вашей собственной системы.
Объекты и компоненты хранятся в отдельном менеджере.
Стратегия, которую вы упоминаете, заставляя сущности хранить свои собственные компоненты (
entity.position
), отчасти противоречит теме компонентов сущностей, но она полностью приемлема, если вы чувствуете, что это имеет смысл.источник
EntityManager
где хранятся вещи.«Эта» статья - не та, с которой я особенно согласен, поэтому я думаю, что мой ответ будет несколько критичным.
Идея состоит не в том, чтобы гарантировать, что в вашей программе нет ничего, кроме идентификатора сущности, компонента или системы, а в том, чтобы гарантировать, что данные и поведение сущности создаются с помощью составления объектов, а не с использованием сложного дерева наследования или, что еще хуже, попыток положить всю возможную функциональность в один объект. Для реализации этих компонентов и систем у вас наверняка будут нормальные данные, такие как векторы, которые в большинстве языков лучше всего представлены в виде класса.
Не обращайте внимания на то, что в статье говорится, что это не ООП - это такой же ООП, как и любой другой подход. Когда большинство компиляторов или языковых сред выполнения реализуют методы объекта, это в основном аналогично любой другой функции, за исключением того, что есть скрытый аргумент, называемый
this
илиself
, который является указателем на место в памяти, где хранятся данные этого объекта. В системе, основанной на компонентах, идентификатор объекта можно использовать для определения того, где находятся соответствующие компоненты (и, следовательно, данные) для данного объекта. Таким образом, идентификатор объекта эквивалентен указателю this / self, и концепции в основном одно и то же, только немного переставленные.Хорошо. Методы являются эффективным способом организации вашего кода. Важно отойти от идеи «избегать ООП» - избегать повсеместного использования наследования для расширения функциональности. Вместо этого разбейте функциональность на компоненты, которые можно объединить, чтобы сделать то же самое.
Идея системы, основанной на компонентах, заключается в том, что у вас не будет отдельных классов для них, но у вас будет один класс Object / Entity, а изображение будет Object / Entity с ImageRenderer, а Animations будет Object / Сущность, у которой есть AnimationRenderer и т. Д. Соответствующие системы будут знать, как визуализировать эти компоненты, и поэтому не потребуется никакого базового класса с методом Draw ().
Конечно, но это не очень хорошо работает с компонентами. У вас есть 3 варианта:
Вы можете хранить компоненты в системе. Массив не проблема, но где вы храните компонент.
источник
Вектор - это данные. Функции больше похожи на служебные функции - они не являются специфичными для данного экземпляра данных, их можно применять ко всем векторам независимо. Хороший способ думать об этом: можно ли переписать эти функции как статические методы? Если это так, это просто утилита.
источник