Состояние игры и обработка ввода в компонентных системах

16

Мой вопрос:

Как я могу обрабатывать игровые состояния в моей системе сущностей, не прибегая к хранению стека игровых состояний?

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

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

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

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

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

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

У кого-нибудь есть еще идеи, как это сделать?

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

elFarto
источник
6
Я делаю это так: у меня есть Screens, MenuScreen PauseScreen GameScreen, каждый экран может создавать свой собственный Мир (контейнер для сущностей) и системы (например, RenderingSystem), а затем в GameScreen я создаю World, Entity with CameraComponent и устанавливаю CameraComponent.RenderTarget в фон экранов. Таким образом, я могу добавить InventoryScreen, который будет иметь собственные сущности и системы (например, упрощенный рендер). Ввод может быть передан с экрана на мир, поэтому ваш пользовательский интерфейс будет решать, будет ли он передавать ввод на экран (если он сфокусирован, видим и т. Д.) И будет ли он передавать ввод миру и объектам
Кикаймару,
2
@ Byte56 Не совсем, только первый связан с игровыми состояниями (два других - это состояния внутри сущностей), и это не решает ту же проблему, что и я. Когда игра находится в состоянии паузы, что-то должно произойти с системой ввода, чтобы она перестала посылать сообщения о движении объекту игрока (например), я просто не могу найти хороший способ сделать это.
эльФарто
1
Хорошо, тогда посчитайте их связанными. Хороший вопрос.
MichaelHouse
1
Еще кое-что, что нужно учитывать, что раньше раздражало мои системы на основе компонентов: многоуровневый интерфейс. Диалог, появляющийся на мировых или многоуровневых экранах. Это дошло до сих пор в каждой игре, которую я сделал, поэтому я бы сказал, чтобы убедиться, что вы нашли подход, который может решить эту проблему.
АБР

Ответы:

14

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

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

Таким образом, другие ваши компоненты не зависят от конкретных игровых планшетов / входов (BUTTON_A, BUTTON_B против BUTTON_X, BUTTON_O ...), но все они реагируют на одни и те же намерения (IntentRun, IntentReload ...).

Другое преимущество состоит в том, что система намерений может знать о добавлении / удалении доступных контроллеров, поскольку она может отправлять намерения любому подписчику даже вне симуляции, с которой вы можете справиться AddPlayer(controllerID).

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

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

Контекст может быть приоритетным, то есть:

  • SimulationAvailableContext отправляет намерения в симуляцию, пока она доступна (но не работает), например, переместить камеру, уменьшить масштаб, добавить / удалить плеер ...
  • SimulationRunningContext отправляет намерения на симуляцию, пока она не приостановлена, перемещает игрока, отправляет юнит в позицию, стреляет ...

Таким образом, вы можете добавлять и удалять контексты, которые в настоящее время актуальны.

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

Один из способов, который часто используется для воспроизведения / приостановки симуляции игры без прерывания обновлений, не связанных с симуляцией, - это использование разных наборов времени. то есть GenericSystem::onTime(Long time, Long deltaTime, Long simTime, Long simDeltaTime).

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

койот
источник
Мне нравится тот факт, что для этого не нужно вызывать набор функций «Изменено состояние» на всех объектах. Вы должны беспокоиться о неправильных намерениях, отправляемых в неподходящее время, но я думаю, что это лучше, чем альтернатива.
Томас Марнелл
ваши сущности могут игнорировать намерения, такие как Прыжок, в то время как их состояние не позволяет им прыгать (то есть не касаясь земли). но им не нужно беспокоиться о получении таких намерений, пока игра приостановлена.
Койот
Я уже думал о том, чтобы позволить сущности сообщать системе ввода, в каких состояниях доставлять сообщения, но я не думал ставить эти состояния на сам ввод, что является хорошей идеей. Также приятно разделять время и simTime.
ElFarto
Вы должны избегать раздувания вашего состояния, связанного с моделированием, с вещами, не связанными с моделированием. Переместите весь пользовательский интерфейс и код, связанный с игроком, как можно дальше от самой симуляции, и в симуляции концентрируйтесь только на намерениях.
Койот
Эй, @Coyote, эта система звучит очень интересно. Не могли бы вы предоставить больше информации, ответив на этот вопрос ? Благодарность!
Пек
2

Как насчет создания глобальной системы событий, а затем иметь компонент слушателя событий для каждой вашей сущности? После события «Изменение состояния игры» вы можете поиграть с компонентами индивидуально для каждой конкретной сущности.

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

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

То, что я только что объяснил, является практическим примером вашего четвертого решения.

karmalis
источник