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

Проблема

Я хочу, чтобы система была: простой, полной, развязанной.

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

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

Некоторые известные подходы:

  • Построение Конечного автомата: мощный, надежный, но слишком строгий, и с ним может быть трудно справиться, если он увеличится в размерах.
  • Reactive Game State: это очень интересный и эффективный подход, хотя он кажется сложным для небольших случаев использования.
  • Длинные цепочки if-else (или переключатель): полезны только для небольших или ограниченных по времени проектов IMHO.

Итак, что я ищу? Я хочу, чтобы система была:

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

Прежде чем двигаться дальше, я предполагаю, что вы знаете о:

Начните с простого

Состояния игры можно увидеть в виде логической комбинации этапов (строки)

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

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

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

Пример

Если мне нужно проверить, нахожусь ли я в игровом состоянии «могу пройти на второй этаж» (S), мне нужно проверить, есть ли у меня этапы «вход на первый этаж» (A), «найден ключ от второго этажа» (B). Так:

S = A И B

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

Первая проблема: считать

С системой, которую я только что описал, подсчет — непростая задача. На самом деле мне пришлось бы добавлять новый этап для каждого увеличения счетчика: «one_hit», «two_hits» и т. д.

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

Типы сцен

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

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

👀 Посмотрите мою реализацию этой системы здесь!

А агрегация?

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

Мы можем сделать это проще, добавив состояния контрольных точек.

Пример

Состояние второго уровня определяется как:

Второй уровень = победить босса первого уровня Иподняться по лестнице второго уровня

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

Вступайте продюсеры сцены!

Работа продюсера сцены состоит в том, чтобы добавить новую сцену, если выполняется условие. Условие будет проверяться на каждом кадре, где этапы изменились.

Вся система теперь может выглядеть как FSM, хотя она добавляет дополнительный уровень: этапы; нам не нужно проверять изменение состояния игровых объектов вокруг игры. Более того, изменение состояния на самом деле не приводит к каким-либо побочным эффектам, только игровые объекты, которые наблюдают за этапами, улавливают изменение и в конечном итоге действуют в соответствии с ним.

👀 посмотрите реализацию здесь!

Это медленно?

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

«Но мне нужно миллиард стадий в моей игре!» Не волнуйтесь, у нас еще есть туз в рукаве! ♣️

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

Выводы

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

Это мой взгляд на обработку игровых состояний, а ваш?