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

Дизайн игры в стиле OO

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

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

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

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

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

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

  • Передайте GameContext каждому объекту, из которого объект может удалить себя, если потребуется, или обновить состояние игры (например, "не работает", если игрок убит).
  • Зарегистрируйте каждый GameEntity в качестве слушателя в центральном классе Game и используйте ориентированный на события подход; например столкновение приведет к тому, что a CollisionEvent будет уволен двум участникам столкновения.
4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

Сверхпрощание здесь подражает вашему примеру, чтобы сделать мою мысль:

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();
     }
  }
}

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

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

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

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

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

Ответ 4

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

Я сам выучил пару трюков.

Ответ 5

Я только публикую это как ответ, потому что я еще не могу комментировать другие сообщения.

В качестве дополнения к замечательному ответу Эвана Роджерса вам могут быть интересны следующие статьи:

http://www.gamasutra.com/blogs/MeganFox/20101208/6590/Game_Engines_101_The_EntityComponent_Model.php

http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/

Ответ 6

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

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