Альтернатива Game State System?

30

Насколько я могу судить, большинство игр имеют своего рода «систему состояний игры», которая переключается между различными состояниями игры; это могут быть такие вещи, как «Intro», «MainMenu», «CharacterSelect», «Loading» и «Game».

С одной стороны, имеет смысл разделить их на государственную систему. В конце концов, они несопоставимы и в противном случае должны были бы быть в большом выражении switch, что, очевидно, является грязным; и они, безусловно, хорошо представлены государственной системой. Но в то же время я смотрю на состояние «Игра» и задаюсь вопросом, есть ли что-то не так с этим подходом системы состояний. Потому что это как слон в комнате; это ОГРОМНО и очевидно, но никто не ставит под сомнение подход к системе игровых состояний.

Мне кажется глупым, что «Игра» ставится на один уровень с «Главным меню». Тем не менее, нет способа разрушить состояние «игры».

Является ли система состояний игры лучшим способом? Есть ли какая-то другая, лучшая техника для управления "игровым состоянием"? Разве это нормально иметь начальное состояние, которое рисует фильм и слушает ввод, а затем состояние загрузки, которое зацикливается на менеджере ресурсов, и затем состояние игры, которое делает практически все ? Вам это тоже не кажется неуравновешенным? Я что-то пропустил?

Ricket
источник

Ответы:

30

Я думаю, что вы просто спорите семантику здесь. Он называется Game State, потому что он ведет себя как конечный автомат с конечным числом состояний и переходов между ними. «Игра» в «Системе состояний игры» относится ко всей системе, где «Загрузка», «Главное меню» и т. Д. Являются состояниями игры. Их можно легко назвать «сцены» или «экраны» или «уровни». Это просто семантика.

Я не уверен, что строгий FSM применяется больше. В моих реализациях я называю состояния «Screens» и позволяю им быть наращиваемыми, т.е. Экраны могут быть нарисованы поверх других экранов, контролируя, обновляются ли экраны под ними или нет. Таким образом, я могу иметь несколько активных экранов одновременно с автономной логикой и кодом, характерным для этого экрана, не беспокоясь о каком-либо другом экране.

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

DrDeth
источник
это именно то, как это должно быть сделано. Экраны должны содержать несколько других экранов в древовидной архитектуре. Таким образом, ваша программа содержит игровой экран, который содержит экран меню паузы, который содержит экран настроек звука и экран настроек игры. и т.д.
Иэн
1
Я хотел бы увидеть пример исходного кода для этого! (Желательно на C ++ / C # / Java)
Zolomon
1
К сожалению, на данный момент у меня есть только производственный код, но в примере XNA Game State есть похожая концепция: creators.xna.com/en-GB/samples/gamestatemanagement
DrDeth
7

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

тетрада
источник
4

Мне всегда нравится думать о каждом "государстве" как о "сцене". Итак, вступительное видео - это сцена, просто статичная. Кредиты сцена. Меню это сцена. Единственная разница между ними - уровень интерактивности и игровой логики.

Энтони
источник
3

У меня тоже проблемы с этим, на самом деле.

Допустим, у вас есть игра.

Вместо того , чтобы «Игра» это состояние как «Загрузка», «Главное меню» и т.д. - IMO, лучше пусть Игра имеет несколько состояний:

«Загрузка» - «Показ меню» - «Приостановлено» и т. Д.

Игра все еще работает, но когда она показывает главное меню, она будет в режиме «показывать меню».

И когда Game не находится в каком-либо конкретном состоянии, он просто запущен.

Это имеет гораздо больше смысла, по крайней мере для меня. :)

jacmoe
источник
Я согласен. Никто не хочет выходить из Game-State только для того, чтобы войти в Pause-State . С другой стороны: это все еще государственная система ... просто вложенная :)
bummzack
2

Онлайн-программа (в традиционном смысле онлайн, то есть постоянно работающая и реагирующая на ввод, а не связанная с Интернетом) обычно состоит из 3 вещей:

  • сбор и обработка ввода
  • обновление логики
  • выход

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

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

Конечно, если у вас действительно есть отдельные типы игрового процесса (например, пример RPG - Карта мира, Карта города, Катсцена, Битва), с различными входами, обновлениями и выходом, нет причины, по которой вы не могли бы иметь несколько состояний тоже вместо 1 состояния игры. Но это зависит от вашей игры.

Kylotan
источник
1

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

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

В моей игре у меня есть:

Менеджер выполнения , который инициализирует приложение (игру), загружает ресурсы, освобождает ресурсы при выходе из приложения и т. Д. Он инициализирует Application Engine, GameViewEngine, GameLogicEngine.

Game State Manager , который находится в GameLogicEngine и отвечает за управление вещами, относящимися к основному циклу игры: обнаружение столкновений, вычисление физики, чтение с клавиатуры, математические операции и т. Д.

Изначально у меня был, как правило, только один Game State Manager, который был частью моего GameLogicEngine. Однако у меня возникли некоторые трудности с управлением инициализацией основных подсистем (GameLogic, ApplicationEngine, ...). Это могло быть сделано, но это было более грязно, IMO.

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

Bunkai.Satori
источник
0

Переименуйте состояние «Game» во что-то вроде «Gameplay». Тогда ваша логика кажется лучше; Вы прекращаете играть, чтобы перейти в меню: вы выходите из состояния Gameplay, чтобы перейти в состояние MainMenu.

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

Коммунистическая утка
источник
0

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

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

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

Саймон
источник
0

Разве это нормально иметь начальное состояние, которое рисует фильм и слушает ввод, а затем состояние загрузки, которое зацикливается на менеджере ресурсов, и затем состояние игры, которое делает практически все? Вам это тоже не кажется неуравновешенным? Я что-то пропустил?

Это прекрасно. Или, по крайней мере, это улучшение по сравнению с «большим уродливым переключателем в зависимости от состояния игры».

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

Если у вас достаточно абстрагированный конечный автомат, вы можете повторно использовать его как для игрового объекта, так и для своего AI; внезапно вы не «вкладываете» много усилий в игровое состояние - вместо этого вы снова используете код, который вы использовали в любом случае.

Получается бесстыдная самостоятельная вставка: я реализовал такой конечный автомат в моей игровой библиотеке Lua MiddleClass (конкретно, надстройка под названием MindState). Вот как вы делаете это с Game State .

egarcia
источник
0

Другой подход к этому - использовать концепцию из мира функционального программирования, называемую « Дискриминационный союз» . Хотя они обычно встречаются на языках FP, вы можете эмулировать их, используя классы .

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

Например:

type GameState =
  | Menu of MenuState
  | Playing of SimulationState

Здесь наш GameStateтип может быть либо Menuили Playing. Если это так Menu, то он будет содержать MenuStateобъект. Если это так Playing, то он будет содержать SimulationStateобъект.

Чтобы обновить, мы бы matchо состоянии и вызвали другую функцию соответственно:

let update gameTime = 
  let nextState = 
    match gameState with
    | Menu menuState -> updateMenu gameTime menuState
    | Playing simulationState -> updateSimulation gameTime simulationState

  // Mutate the current state
  gameState <- nextState

И аналогично для рендеринга:

let render gameTime = 
  let nextState = 
    match gameState with
    | Menu menuState -> renderMenu menuState
    | Playing simulationState -> renderSimulation simulationState

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

sdgfsdh
источник