Игровой дизайн способом OO

Я разрабатываю простую игру, которая использует Java 2D и ньютонова физика. В настоящее время мой основной "игровой цикл" смотрит что-то как:

do {
  for (GameEntity entity : entities) {
    entity.update(gameContext);
  }

  for (Drawable drawable : drawables) {
    drawable.draw(graphics2d);
  }
} while (gameRunning);

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

Мой вопрос: Что лучший способ состоит в том, чтобы достигнуть этого объектно-ориентированным способом? Все примеры, которые я видел до сих пор, соединяются, игровой цикл в класс Бога назвал что-то как Game, который выполняет шаги: обнаружьте коллизии, check-if-bad-guy-killed, check-if-player-killed, перекрашивание, и т.д. и инкапсулирует все игровое состояние (остающиеся жизни, и т.д.). Другими словами, это является очень процедурным, и вся логика находится в Игровом классе. Кто-либо может рекомендовать лучший подход?

Вот опции, о которых я думал до сих пор:

  • Передача a GameContext к каждому объекту, от которого объект может удалить себя при необходимости или обновить игровое состояние (например, к "не выполнению", если плеер уничтожается).
  • Зарегистрируйте каждого GameEntity как слушатель центрального Game класс и проявляет ориентированный подход события; например, коллизия привела бы к a CollisionEvent будучи запущенным в эти двух участников коллизии.
21
задан Adamski 10 February 2010 в 17:43
поделиться

5 ответов

Я тесно сотрудничал с двумя коммерческими игровыми движками, и они следуют схожему шаблону:

  • Объекты представляют компоненты или аспекты игровой сущности (например, физическую, Renderable, что угодно), а не всю сущность. Для каждого типа компонентов существует гигантский список компонентов, по одному для каждого экземпляра сущности, в котором есть компонент.

  • Сам тип «игровой сущности» - это просто уникальный идентификатор. Каждый гигантский список компонентов имеет карту для поиска компонента (если таковой существует), соответствующего идентификатору объекта.

  • Если компонент требует обновления, он вызывается службой или системным объектом. Каждая услуга обновляется прямо из игрового цикла. В качестве альтернативы вы можете вызывать службы из объекта планировщика, который определяет порядок обновления из графа зависимостей.

Вот преимущества этого подхода:

  • Вы можете свободно комбинировать функциональные возможности без написания новых классов для каждой комбинации или использования сложных деревьев наследования .

  • Практически нет функциональных возможностей, которые вы можете использовать в отношении всех игровых объектов, которые вы могли бы добавить в базовый класс игрового объекта (что делает свет {{1} } имеют что-то общее с гоночным автомобилем или скайбоксом?)

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

14
ответ дан 29 November 2019 в 21:32
поделиться

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

Мы также сделали физическое моделирование в отдельном объекте, который можно было изменять на лету. Это позволяло нам легко манипулировать гравитацией и т.д.

Мы активно использовали код, управляемый событиями (паттерн слушателя), и много-много таймеров.

Например, у нас был базовый класс для пересекающегося объекта, который мог слушать событие столкновения. Мы подклассифицировали его в ящик здоровья. При столкновении, если он попадал в объект игрока, он посылал команду коллидеру, что тот должен набрать здоровье, посылал сообщение для трансляции звука всем, кто мог его услышать, деактивировал столкновения, активировал анимацию для удаления графики из графа сцены и устанавливал таймер для повторного включения позже. Звучит сложно, но на самом деле это было не так.

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

6
ответ дан 29 November 2019 в 21:32
поделиться

Я пишу свои собственные движки (сырые и грязные), но предварительно собранный движок с приличной объектно-ориентированной моделью - это Ogre . Я бы рекомендовал взглянуть на него (это объектная модель / API). Назначение узлов - это немного забавно, но чем больше вы смотрите на это, тем лучше становится смысл. Он также очень хорошо задокументирован с множеством примеров рабочих игр.

Я сам научился парочке уловок.

2
ответ дан 29 November 2019 в 21:32
поделиться

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

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

1
ответ дан 29 November 2019 в 21:32
поделиться

Я не согласен с тем, что, поскольку у вас есть основной класс Game, вся логика должна выполняться в этом классе.

Чрезмерное упрощение, имитирующее ваш пример, чтобы подчеркнуть мою точку зрения:

mainloop:
  moveEntities()
  resolveCollisions()   [objects may "disappear"/explode here]
  drawEntities()        [drawing before or after cleanEntitites() ain't an issue, a dead entity won't draw itself]
  cleanDeadEntities()

Теперь у вас есть класс Bubble:

Bubble implements Drawable {

handle( Needle needle ) {
    if ( needle collide with us ) {
        exploded = true;
    }
}

draw (...) {
   if (!exploded) {
      draw();
     }
  }
}

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

Я почти уверен, что даже в вашем случае вся логика, связанная с движением, не происходит в основном классе.

Я не согласен с вашим утверждением, которое вы написали жирным шрифтом, что «вся логика происходит в основном классе».

Это просто неверно.

Что касается хорошего дизайна: если вы можете легко предоставить другой «вид» своей игры (например, мини-карту) и если вы можете легко закодировать «покадровый идеальный повторитель», тогда ваш дизайн вероятно, не так уж и плохо (то есть: записав только входные данные и время, в которое они произошли, вы сможете воссоздать игру именно так, как в нее играли. Именно так Age of Empires, Warcraft 3 и т. д. делают их повтор : записываются только вводимые пользователем данные и время, в которое они произошли [именно поэтому файлы воспроизведения обычно такие крошечные]).

3
ответ дан 29 November 2019 в 21:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: