Составная система шаблон / сущность и традиционный ООП - PullRequest
16 голосов
/ 09 февраля 2011

Я работаю над небольшой игрой, написанной на Java (но вопрос не зависит от языка).Поскольку я хотел исследовать различные шаблоны проектирования, я застрял на Composite pattern / Entity system (которую я первоначально прочитал о здесь и здесь ) какальтернатива типичному глубокому иерархическому наследованию.

Теперь, после написания нескольких тысяч строк кода, я немного запутался.Я думаю, что понимаю шаблон и мне нравится его использовать.Я думаю, что это очень круто и Starbucks-иш, но он чувствует, что польза, которую он дает, несколько недолговечна и (что меня больше всего раздражает) сильно зависит от вашей детализации.: enter image description here

Мне нравится, как объекты (игровые объекты или как вы хотите их называть) имеют минимальный набор компонентов, и логическая идея состоит в том, что вы могли бы написать код, который выглядит примерно так:

BaseEntity Alien = new BaseEntity();
BaseEntity Player = new BaseEntity();

Alien.addComponent(new Position(), new Movement(), new Render(), new Script(), new Target());
Player.addComponent(new Position(), new Movement(), new Render(), new Script(), new Physics());

.. что было бы действительно хорошо ... но в РЕАЛЬНОСТИ код в конечном итоге выглядит примерно так:

BaseEntity Alien = new BaseEntity();
BaseEntity Player = new BaseEntity();

Alien.addComponent(new Position(), new AlienAIMovement(), new RenderAlien(), new ScriptAlien(), new Target());
Player.addComponent(new Position(), new KeyboardInputMovement(), new RenderPlayer(), new ScriptPlayer(), new PhysicsPlayer());

Кажется, что в итоге у меня есть некоторые очень специализированные компоненты, которыесоставлен из меньших компонентов.Часто мне приходится делать некоторые компоненты, которые имеют зависимости от других компонентов.В конце концов, как вы можете сделать, если у вас нет позиции?Не только это, но и способ, которым вы в конечном итоге оказываете игрока против иностранца или гранаты, может быть принципиально другим.У вас не может быть ОДНОГО компонента, который диктует все три, если только вы не сделаете очень большой компонент (в этом случае ... почему вы все равно используете составной шаблон?)

Чтобы дать другой реальный пример.В моей игре есть персонажи, которые могут экипировать различное снаряжение.Когда снаряжение экипировано, некоторые статистические данные меняются, а также то, что отображается визуально.Вот как выглядит мой код прямо сейчас:

billy.addControllers(new Movement(), new Position(), new CharacterAnimationRender(), new KeyboardCharacterInput());

billy.get(CharacterAnimationRender.class).setBody(BODY.NORMAL_BODY);
billy.get(CharacterAnimationRender.class).setFace(FACE.BLUSH_FACE);
billy.get(CharacterAnimationRender.class).setHair(HAIR.RED_HAIR);
billy.get(CharacterAnimationRender.class).setDress(DRESS.DRAGON_PLATE_ARMOR);

Вышеуказанный CharacterAnimationRender.class влияет только на то, что отображается ВИЗУАЛЬНО.Поэтому мне явно нужно создать еще один компонент, который обрабатывает статистику снаряжения.Однако зачем мне делать что-то вроде:

billy.addControllers(new CharacterStatistics());

billy.get(CharacterAnimationRender.class).setBody(BODY.NORMAL_BODY);
billy.get(CharacterStatistics.class).setBodyStats(BODY_STATS.NORMAL_BODY);

Когда я могу просто сделать CharacterGearStuff контроллер / компонент, который обрабатывает ОБА как распределение статистики, так и визуальные изменения?

В итоге, я не уверен, как это должно способствовать повышению производительности, поскольку, если вы не хотите обрабатывать все вручную, вам все равно придется создавать «метакомпоненты», которые зависят от 2+ компонентов (и модифицировать / перекрестно модифицировать все ихподкомпоненты - возвращая нас к ООП).Или, может быть, я думаю об этом совершенно неправильно.Я?

Ответы [ 4 ]

6 голосов
/ 13 февраля 2011

Похоже, вы немного неправильно поняли шаблон компонента.

Компоненты только данные, без кода. Если в вашем компоненте есть код, он больше не является компонентом - это нечто более сложное.

Так, например, у вас должна быть возможность обмениваться своими CharacterAnimationRender и CharacterStatistics, например ::1005*

CharacterStats { int BODY }
CharacterGameStats { ...not sure what data you have that affects gameplay, but NOT the rendering... }
CharacterVisualDetails { int FACE, int HAIR }

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

...

Возвращаясь к вашим проблемам в начале, с которыми вы в конечном итоге получаете:

Alien.addComponent(new Position(), new AlienAIMovement(), new RenderAlien(), new ScriptAlien(), new Target());
Player.addComponent(new Position(), new KeyboardInputMovement(), new RenderPlayer(), new ScriptPlayer(), new PhysicsPlayer());

... это прекрасно. Предполагая, что вы написали эти компоненты правильно, вы разбили данные на небольшие куски, которые легко читать / отлаживать / редактировать / кодировать.

Однако это наводит на мысль, потому что вы не указали, что находится внутри этих компонентов ... например AlienAIMovement - что в этом? Обычно я ожидаю, что у вас будет «AIMovement ()», а затем отредактируете его, чтобы сделать его версией Чужого, например, измените некоторые внутренние флаги в этом компоненте, чтобы указать, что он использует функции «Чужой» в вашей системе ИИ.

2 голосов
/ 16 ноября 2013

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

Все предыдущие ответы кажутся неуклюжими и создают тонны ненужных объектов, ИМО.

2 голосов
/ 09 февраля 2011

Дэвид,

сначала спасибо за ваш идеальный вопрос.

Я понимаю вашу проблему и думаю, что вы не правильно использовали шаблон. Пожалуйста, прочитайте эту статью: http://en.wikipedia.org/wiki/Composite_pattern

Если, например, вы не можете реализовать общий класс Movement и вам нужны AlienAIMovement и KeyboardMovement, вам, вероятно, следует использовать шаблон Visitor. Но прежде чем начать рефакторинг тысяч строк кода, проверьте, можете ли вы сделать следующее.

Есть ли шанс написать класс Movement, который принимает параметр типа BaseEntity? Вероятно, разница между всеми реализациями Movement заключается только в параметре, флаге или около того? В этом случае ваш код будет выглядеть так:

Alien.addComponent(new Position(), new Movement(Alien), new Render(Alien), new Script(Alien), new Target());

Я думаю, что это не так плохо.

Если это невозможно, попробуйте создать экземпляры с использованием фабрики, поэтому

Alien.addComponent(f.createPosition(), f.createMovement(Alien), f.createRender(Alien), f.createRenderScript(Alien), f.createTarget());

Надеюсь, мои предложения помогут.

1 голос
/ 09 февраля 2011

Я думаю, что вы используете неправильный подход здесь. Шаблоны должны быть адаптированы к вашим потребностям, а не наоборот. Простое всегда лучше сложного, и если вы чувствуете, что что-то не работает, это означает, что вы должны сделать несколько шагов назад и, возможно, начать с самого начала.

Для меня это уже запах кода:

BaseEntity Alien = new BaseEntity();
Alien.addComponent(new Position(), new AlienAIMovement(), new RenderAlien(), new ScriptAlien(), new Target());

Я бы ожидал, что объектно-ориентированный код будет выглядеть примерно так:

Alien alien = new AlienBuilder()
    .withPosition(10, 248)
    .withTargetStrategy(TargetStrategy.CLOSEST)
    .build();

//somewhere in the main loop
Renderer renderer = getRenderer();
renderer.render(alien);

Когда вы используете универсальные классы для всех ваших сущностей, у вас будет очень универсальный и сложный в использовании API для работы с вашими объектами.

Кроме того, неправильно иметь такие вещи, как Position, Move и Renderer под одним и тем же компонентным зонтиком. Позиция не является компонентом, это атрибут. Движение - это поведение, а Renderer - это то, что вообще не связано с вашей моделью домена, это часть графической подсистемы. Компонентом может быть колесо для автомобиля, детали кузова и оружие для инопланетянина.

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

...