В настоящее время я перепроектирую свою Entity System для C ++, и у меня много менеджеров. В моем дизайне у меня есть эти классы, чтобы связать мою библиотеку. Я слышал много плохих вещей, когда речь шла о классах «менеджера», возможно, я не правильно назвал свои классы. Однако я понятия не имею, как еще их назвать.
Большинство менеджеров в моей библиотеке состоят из этих классов (хотя они немного различаются):
- Контейнер - контейнер для объектов в менеджере
- Атрибуты - атрибуты для объектов в менеджере
В моем новом дизайне для моей библиотеки у меня есть эти конкретные классы, чтобы связать мою библиотеку вместе.
ComponentManager - управляет компонентами в Entity System
- ComponentContainer
- ComponentAttributes
- Сцена * - ссылка на сцену (см. Ниже)
SystemManager - управляет системами в Entity System
- SystemContainer
- Сцена * - ссылка на сцену (см. Ниже)
EntityManager - управляет объектами в Entity System
- EntityPool - пул сущностей
- EntityAttributes - атрибуты сущности (это будет доступно только для классов ComponentContainer и System)
- Сцена * - ссылка на сцену (см. Ниже)
Сцена - связывает всех менеджеров вместе
- ComponentManager
- SystemManager
- EntityManager
Я думал просто поместить все контейнеры / пулы в сам класс Scene.
т.е.
Вместо этого:
Scene scene; // create a Scene
// NOTE:
// I technically could wrap this line in a createEntity() call in the Scene class
Entity entity = scene.getEntityManager().getPool().create();
Было бы так:
Scene scene; // create a Scene
Entity entity = scene.getEntityPool().create();
Но я не уверен. Если бы я делал последнее, это означало бы, что у меня было бы много объектов и методов, объявленных внутри моего класса Scene.
ЗАМЕТКИ:
- Система сущностей - это просто дизайн, который используется для игр. Он состоит из 3 основных частей: компонентов, объектов и систем. Компоненты - это просто данные, которые могут быть «добавлены» к объектам, чтобы объекты были различимыми. Сущность представлена целым числом. Системы содержат логику для сущности с конкретными компонентами.
- Причина, по которой я меняю свой дизайн для своей библиотеки, заключается в том, что я думаю, что он может быть изменен довольно сильно, мне не нравится ощущение / поток к нему в данный момент.
источник
Ответы:
DeadMG точно разбирается в особенностях вашего кода, но я чувствую, что он упускает пояснения. Также я не согласен с некоторыми из его рекомендаций, которые не выполняются в некоторых конкретных контекстах, таких как, например, разработка высокопроизводительных видеоигр. Но он глобально прав, что большая часть вашего кода сейчас бесполезна.
Как говорит Данк, классы Manager называются так, потому что они «управляют» вещами. «управлять» - это как «данные» или «делать», это абстрактное слово, которое может содержать почти все.
Я был в основном в том же месте, что и вы, около 7 лет назад, и я начал думать, что в моем мышлении было что-то не так, потому что было так много усилий, чтобы код ничего не делал.
Что я изменил, чтобы исправить это, так это изменить словарный запас, который я использую в коде. Я полностью избегаю универсальных слов (если это не универсальный код, но редко, когда вы не создаете универсальные библиотеки). Я избегаю называть любой тип «Менеджер» или «Объект».
Воздействие прямо в коде: оно заставляет вас найти правильное слово, соответствующее реальной ответственности вашего типа. Если вы чувствуете, что тип делает несколько вещей (поддерживает индекс книг, поддерживает их работу, создает / уничтожает книги), тогда вам нужны разные типы, каждый из которых будет иметь одну ответственность, а затем объедините их в коде, который их использует. Иногда мне нужна фабрика, иногда нет. Иногда мне нужен реестр, поэтому я его настраиваю (используя стандартные контейнеры и умные указатели). Иногда мне нужна система, состоящая из нескольких разных подсистем, поэтому я делю все, что могу, чтобы каждая часть делала что-то полезное.
Никогда не называйте тип «менеджер» и убедитесь, что все ваши типы имеют одну уникальную роль - моя рекомендация. Иногда бывает трудно найти имена, но это одна из самых сложных вещей в программировании.
источник
dynamic_cast
убивает вашу производительность, тогда используйте статическую систему типов - вот для чего она. Это действительно специализированный, а не общий контекст, когда нужно свернуть свой RTTI, как это сделал LLVM. Даже тогда они этого не делают.template <typename T> class Class { ... };
на самом деле для назначения идентификаторов для различных классов (а именно, пользовательских компонентов и пользовательских систем). Назначение идентификатора используется для хранения компонентов / систем в векторе, поскольку карта может не обеспечивать производительность, необходимую для игры.Ну, я прочитал код, на который вы ссылались, и ваше сообщение, и мое честное резюме заключается в том, что большинство из них в основном совершенно бесполезны. Сожалею. Я имею в виду, у вас есть весь этот код, но вы ничего не достигли . Вообще. Я собираюсь углубиться здесь, так что терпите меня.
Давайте начнем с ObjectFactory . Во-первых, указатели на функции. Это без сохранения состояния, единственный полезный способ создания объекта без сохранения состояния -
new
и вам не нужна фабрика для этого. Создание объекта - это то, что полезно, а указатели функций для этой задачи не нужны. И, во-вторых, каждый объект должен знать, как уничтожить себя , а не искать точный создатель для его уничтожения. Кроме того, вы должны возвращать умный указатель, а не необработанный указатель, для исключения и безопасности ресурсов вашего пользователя. Весь ваш класс ClassRegistryData простоstd::function<std::unique_ptr<Base, std::function<void(Base*)>>()>
, но с вышеупомянутыми дырами. Это не должно существовать вообще.Затем у нас есть AllocatorFunctor - уже есть стандартные интерфейсы распределителя - не говоря уже о том, что они являются просто оболочками
delete obj;
иreturn new T;
- что опять-таки уже предоставлено, и они не будут взаимодействовать с какими-либо существующими или пользовательскими распределителями памяти, которые существуют.И ClassRegistry - это просто,
std::map<std::string, std::function<std::unique_ptr<Base, std::function<void(Base*)>>()>>
но вы написали класс для него вместо использования существующей функциональности. Это то же самое, но совершенно ненужное глобальное изменяемое состояние с кучей дурацких макросов.Здесь я хочу сказать, что вы создали несколько классов, в которых вы могли бы заменить всю функциональность, созданную на основе стандартных классов, а затем сделать их еще хуже, сделав их глобально изменяемым состоянием и задействовав несколько макросов, что является худшее, что вы могли бы сделать.
Тогда у нас есть опознаваемый . Здесь много похожей истории -
std::type_info
уже выполняется работа по определению каждого типа в качестве значения времени выполнения, и это не требует излишних шаблонов со стороны пользователя.Проблема решена, но без каких-либо двухсот предыдущих макросов и прочего. Это также помечает ваш IdentifiableArray как не что иное, как
std::vector
вы, но вы должны использовать подсчет ссылок, даже если было бы лучше уникальное владение или отсутствие владения.О, и я упоминал, что Types содержит кучу абсолютно бесполезных typedefs? Вы буквально не получаете никаких преимуществ по сравнению с непосредственным использованием необработанных типов и теряете значительную часть читабельности.
Далее идет ComponentContainer . Вы не можете выбрать владельца, у вас не может быть отдельных контейнеров для производных классов различных компонентов, вы не можете использовать собственную семантику времени жизни. Удалить без раскаяния.
Теперь, ComponentFilter может стоить немного, если вы много рефакторинг. Что-то типа
Это не касается классов, производных от тех, с которыми вы пытаетесь работать, но и ваших тоже.
В остальном это почти та же история. Здесь нет ничего, что нельзя было бы сделать намного лучше без использования вашей библиотеки.
Как бы вы избежали менеджеров в этом коде? Удалите в основном все это.
источник
typeid
илиdynamic_cast
. Спасибо за отзыв, я ценю это.Проблема с классами Manager заключается в том, что менеджер может делать все что угодно. Вы не можете прочитать имя класса и знать, что делает класс. EntityManager .... Я знаю, что он что-то делает с сущностями, но кто знает, что. SystemManager ... да, он управляет системой. Это очень полезно.
Я думаю, что ваши менеджеры, вероятно, делают много вещей. Бьюсь об заклад, если бы вы разделили эти вещи на тесно связанные функциональные части, то вы, вероятно, сможете придумать имена классов, связанные с функциональными частями, которые любой может сказать, что они делают, просто взглянув на имя класса. Это значительно облегчит поддержание и понимание дизайна.
источник
Я на самом деле не думаю, что
Manager
это так плохо в этом контексте, пожав плечами. Если есть одно место, где я думаю, чтоManager
это простительно, это было бы для системы сущности-компонента, поскольку она действительно « управляет » временем жизни компонентов, сущностей и систем. По крайней мере, здесь это более значимое имя, чем, скажем,Factory
которое не имеет особого смысла в этом контексте, так как ECS действительно делает немного больше, чем создает вещи.Я полагаю, вы могли бы использовать
Database
, какComponentDatabase
. Или вы могли бы просто использоватьComponents
,Systems
иEntities
(это то, что я лично использую, и в основном только потому, что мне нравятся краткие идентификаторы, хотя, по общему признанию, они передают даже меньше информации, возможно, чем «Менеджер»).Одна часть, которую я нахожу немного неуклюжей, это:
Это очень много вещей,
get
прежде чем вы сможете создать сущность, и кажется, что дизайн немного утечет детали реализации, если вам нужно знать о пулах сущностей и «менеджерах» для их создания. Я думаю, что такие вещи, как «пулы» и «менеджеры» (если вы придерживаетесь этого имени) должны быть деталями реализации ECS, а не чем-то, что доступно клиенту.Но не знаю, я видел некоторые очень воображения и общие виды использования
Manager
,Handler
,Adapter
и названия такого рода, но я думаю , что это разумно простительно использовать случай , когда вещь под названием «Менеджер» фактически отвечает за «управление» ( "контроль? "," обработка? ") время жизни объектов, ответственность за создание и уничтожение и обеспечение доступа к ним по отдельности. Это самое простое и интуитивно понятное использование «Менеджера», по крайней мере, для меня.Тем не менее, одна общая стратегия, чтобы избежать «Менеджер» на ваше имя, это просто взять часть «Менеджер» и добавить
-s
в конце. :-D Например, допустим, у вас естьThreadManager
и он отвечает за создание потоков, предоставление информации о них, доступ к ним и т. Д., Но он не объединяет потоки, поэтому мы не можем его вызватьThreadPool
. Ну, в таком случае, просто позвониThreads
. Это то, что я делаю в любом случае, когда я не воображаем.Я немного напомнил, когда аналог «Windows Explorer» назывался «Диспетчер файлов», например:
И я на самом деле думаю, что «Диспетчер файлов» в этом случае более описательный, чем «Проводник Windows», даже если есть какое-то более подходящее имя, которое не включает «Диспетчер». Поэтому, по крайней мере, пожалуйста, не переусердствуйте со своими именами и начните называть такие вещи, как "ComponentExplorer". «ComponentManager», по крайней мере, дает мне разумное представление о том, что эта штука делает, когда кто-то привык работать с двигателями ECS.
источник
Manager
подходит. У класса могут быть проблемы с дизайном , но имя спот-.