Подтвердить что ты не робот

Сложная структура/система сущностей и традиционный ООП

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

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

Вот фотография из второй статьи выше: 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());

.., что было бы очень приятно... но в REALITY код заканчивается тем, что выглядит как

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 влияет только на то, что отображается VISUALLY. Поэтому мне явно нужно сделать еще один компонент, который обрабатывает статистику снаряжения. Однако зачем мне делать что-то вроде:

billy.addControllers(new CharacterStatistics());

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

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

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

4b9b3361

Ответ 1

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

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

Итак, например, вы должны иметь возможность тривиально делиться своими характеристиками CharacterAnimationRender и CharacterStatistics, например:

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

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

...

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

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()", а затем отредактируйте его, чтобы превратить его в версию Alien. измените некоторые внутренние флаги в этом компоненте, чтобы показать его с помощью функций "Alien" в вашей системе AI.

Ответ 2

Дэвид,

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

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

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

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

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

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

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

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

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

Ответ 3

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

Предыдущие ответы кажутся неуклюжими и создают тонны ненужных объектов, IMO.

Ответ 4

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

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

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, Movement и Renderer под одним и тем же зонтиком Component. Позиция не является компонентом, а атрибутом. Движение - это поведение, а Renderer - это то, что вообще не связано с вашей моделью домена, это часть графической подсистемы. Компонент может быть колесом для автомобиля, частей тела и оружия для иностранца.

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