Управление потоком игры
Примечание.
Этот раздел является частью серии руководств по созданию простой игры универсальная платформа Windows (UWP) с помощью DirectX. Эта ссылка задает контекст для ряда.
Теперь у игры есть окно, зарегистрированы некоторые обработчики событий и асинхронно загружены ресурсы. В этом разделе объясняется использование игровых состояний, управление определенными ключевыми игровыми состояниями и создание цикла обновления для игрового двигателя. Затем мы узнаем о потоке пользовательского интерфейса и, наконец, понять больше о обработчиках событий, необходимых для игры UWP.
Игровые состояния, используемые для управления потоком игр
Мы используем игровые состояния для управления потоком игры.
Когда пример игры Simple3DGameDX запускается в первый раз на компьютере, он находится в состоянии, где игра не была запущена. В последующие времена игра выполняется, она может находиться в любом из этих состояний.
- Игра не была запущена, или игра находится между уровнями (высокий показатель равен нулю).
- Игровой цикл работает, и находится в середине уровня.
- Цикл игры не выполняется из-за завершения игры (высокая оценка имеет ненулевое значение).
Ваша игра может иметь столько состояний, сколько он нуждается. Но помните, что его можно завершить в любое время. И когда он возобновляется, пользователь ожидает, что он возобновляется в состоянии, в который он был завершен.
Управление состоянием игры
Таким образом, во время инициализации игры вам потребуется поддерживать холодный запуск игры, а также возобновление игры после остановки его в полете. Пример Simple3DGameDX всегда сохраняет свое состояние игры, чтобы дать впечатление, что он никогда не остановлен.
В ответ на событие приостановки игровой процесс приостановлен, но ресурсы игры по-прежнему находятся в памяти. Аналогичным образом, событие возобновления обрабатывается, чтобы убедиться, что образец игры выбирается в состоянии, в который он находился, когда он был приостановлен или завершен. В зависимости от состояния для игрока отображаются различные параметры.
- Если игра возобновляется на среднем уровне, она появляется приостановлена, а наложение предлагает возможность продолжить.
- Если игра возобновляется в состоянии завершения игры, она отображает высокие оценки и возможность играть в новую игру.
- Наконец, если игра возобновляется до начала уровня, то наложение предоставляет пользователю возможность запуска.
Пример игры не отличается от того, является ли игра холодной, запуск в первый раз без события приостановки или возобновление из приостановленного состояния. Это правильный дизайн для любого приложения UWP.
В этом примере инициализация состояний игры происходит в GameMain::InitializeGameState (структура этого метода показана в следующем разделе).
Ниже приведен блок-схема, помогающий визуализировать поток. Он охватывает как инициализацию, так и цикл обновления.
- Инициализация начинается на начальном узле при проверке текущего состояния игры. Код игры см. в разделе GameMain::InitializeGameState в следующем разделе.
- Дополнительные сведения о цикле обновления см. в разделе "Обновление игрового механизма". Для кода игры перейдите в GameMain::Update.
Метод GameMain::InitializeGameState
Метод GameMain::InitializeGameState вызывается косвенно через конструктор класса GameMain , который является результатом создания экземпляра GameMain в App::Load.
GameMain::GameMain(std::shared_ptr<DX::DeviceResources> const& deviceResources) : ...
{
m_deviceResources->RegisterDeviceNotify(this);
...
ConstructInBackground();
}
winrt::fire_and_forget GameMain::ConstructInBackground()
{
...
m_renderer->FinalizeCreateGameDeviceResources();
InitializeGameState();
...
}
void GameMain::InitializeGameState()
{
// Set up the initial state machine for handling Game playing state.
if (m_game->GameActive() && m_game->LevelActive())
{
// The last time the game terminated it was in the middle
// of a level.
// We are waiting for the user to continue the game.
...
}
else if (!m_game->GameActive() && (m_game->HighScore().totalHits > 0))
{
// The last time the game terminated the game had been completed.
// Show the high score.
// We are waiting for the user to acknowledge the high score and start a new game.
// The level resources for the first level will be loaded later.
...
}
else
{
// This is either the first time the game has run or
// the last time the game terminated the level was completed.
// We are waiting for the user to begin the next level.
...
}
m_uiControl->ShowGameInfoOverlay();
}
Обновление игрового механизма
Метод App::Run вызывает GameMain::Run. В GameMain::Run — это базовый компьютер состояния для обработки всех основных действий, которые может предпринять пользователь. Самый высокий уровень этой машины состояния связан с загрузкой игры, игрой определенного уровня или продолжением уровня после приостановки игры (системой или пользователем).
В примере игры есть 3 основных состояния (представленные перечислением UpdateEngineState), в которых может находиться игра.
- UpdateEngineState::WaitForResources. Цикл игры является велоспортом, не может переходить до тех пор, пока не будут доступны ресурсы (в частности графические ресурсы). По завершении асинхронных задач загрузки ресурсов мы обновим состояние updateEngineState::ResourcesLoaded. Обычно это происходит между уровнями, когда уровень загружает новые ресурсы с диска, с игрового сервера или из облачной серверной части. В примере игры мы имитируем это поведение, так как в этом примере нет дополнительных ресурсов на уровне.
- UpdateEngineState::WaitForPress. Цикл игры является велоспортом, ожидая определенных пользовательских входных данных. Это действие игрока для загрузки игры, запуска уровня или продолжения уровня. Пример кода ссылается на эти вложенные состояния через перечисление PressResultState .
- UpdateEngineState::D ynamics. Цикл игры выполняется с пользователем, играющим. В то время как пользователь играет, игра проверяет наличие 3 условий, на которые он может перейти:
- GameState::TimeExpired. Истечение срока действия для уровня.
- GameState::LevelComplete. Завершение уровня игроком.
- GameState::GameComplete. Завершение всех уровней игроком.
Игра — это просто государственная машина, содержащая несколько небольших компьютеров состояния. Каждое конкретное состояние должно определяться очень конкретными критериями. Переходы из одного состояния в другое должны быть основаны на дискретных входных данных пользователя или системных действиях (например, загрузке графических ресурсов).
При планировании игры рассмотрите возможность извлечь весь поток игры, чтобы убедиться, что вы выполнили все возможные действия, которые пользователь или система могут предпринять. Игра может быть очень сложной, поэтому государственная машина — это мощный инструмент, помогающий визуализировать эту сложность и сделать его более управляемым.
Давайте рассмотрим код цикла обновления.
Метод GameMain::Update
Это структура государственного компьютера, используемая для обновления игрового ядра.
void GameMain::Update()
{
// The controller object has its own update loop.
m_controller->Update();
switch (m_updateState)
{
case UpdateEngineState::WaitingForResources:
...
break;
case UpdateEngineState::ResourcesLoaded:
...
break;
case UpdateEngineState::WaitingForPress:
if (m_controller->IsPressComplete())
{
...
}
break;
case UpdateEngineState::Dynamics:
if (m_controller->IsPauseRequested())
{
...
}
else
{
// When the player is playing, work is done by Simple3DGame::RunGame.
GameState runState = m_game->RunGame();
switch (runState)
{
case GameState::TimeExpired:
...
break;
case GameState::LevelComplete:
...
break;
case GameState::GameComplete:
...
break;
}
}
if (m_updateState == UpdateEngineState::WaitingForPress)
{
// Transitioning state, so enable waiting for the press event.
m_controller->WaitForPress(
m_renderer->GameInfoOverlayUpperLeft(),
m_renderer->GameInfoOverlayLowerRight());
}
if (m_updateState == UpdateEngineState::WaitingForResources)
{
// Transitioning state, so shut down the input controller
// until resources are loaded.
m_controller->Active(false);
}
break;
}
}
Обновление пользовательского интерфейса
Нам нужно держать игрока в курсе состояния системы, и разрешить игровому состоянию изменяться в зависимости от действий игрока и правил, определяющих игру. Многие игры, включая этот пример игры, обычно используют элементы пользовательского интерфейса для представления этой информации игроку. Пользовательский интерфейс содержит представления состояния игры и другие сведения о игре, такие как оценка, ammo или количество оставшихся шансов. Пользовательский интерфейс также называется наложением, так как он отрисовывается отдельно от основного графического конвейера и помещается на вершину трехмерной проекции.
Некоторые сведения о пользовательском интерфейсе также представлены в виде отображения головы (HUD), чтобы разрешить пользователю видеть эту информацию, не принимая глаза полностью от основной игровой области. В примере игры мы создадим это наложение с помощью API Direct2D. Кроме того, мы можем создать это наложение с помощью XAML, который мы обсудим в разделе "Расширение примера игры".
В пользовательском интерфейсе есть два компонента.
- HUD, содержащий оценку и сведения о текущем состоянии игрового процесса.
- Точечный рисунок паузы, который является черным прямоугольником с текстом, наложенным во время приостановленного или приостановленного состояния игры. Это наложение игры. Далее мы обсудим его при добавлении пользовательского интерфейса.
Неудивительно, что наложение также имеет государственный компьютер. Наложение может отображать начальное или игровое сообщение. Это по сути холст, на котором мы можем выводить любую информацию о состоянии игры, которое мы хотим отобразить игроку, пока игра приостановлена или приостановлена.
Отрисовка наложения может быть одним из этих шести экранов в зависимости от состояния игры.
- Экран хода загрузки ресурсов в начале игры.
- Экран статистики игрового процесса.
- Экран начального сообщения уровня.
- Экран с игрой, когда все уровни завершены без истечения времени.
- Экран над игрой, когда время истекает.
- Экран меню приостановки.
Разделяя пользовательский интерфейс от графического конвейера игры, вы можете работать независимо от графического механизма отрисовки игры и значительно снизить сложность кода игры.
Ниже показано, как образец игры структурирование состояния компьютера с наложениями.
void GameMain::SetGameInfoOverlay(GameInfoOverlayState state)
{
m_gameInfoOverlayState = state;
switch (state)
{
case GameInfoOverlayState::Loading:
m_uiControl->SetGameLoading(m_loadingCount);
break;
case GameInfoOverlayState::GameStats:
...
break;
case GameInfoOverlayState::LevelStart:
...
break;
case GameInfoOverlayState::GameOverCompleted:
...
break;
case GameInfoOverlayState::GameOverExpired:
...
break;
case GameInfoOverlayState::Pause:
...
break;
}
}
Обработка событий
Как мы видели в разделе "Определение платформы приложений UWP" игры, многие из методов поставщика представлений класса регистрируют обработчики событий. Эти методы должны правильно обрабатывать эти важные события, прежде чем добавлять механики игр или запускать разработку графики.
Правильная обработка событий в этом вопросе является основой для взаимодействия с приложением UWP. Так как приложение UWP может в любое время быть активировано, деактивировано, изменено, отрезано, отложено, приостановлено или возобновлено, игра должна регистрироваться для этих событий, как только она может, и обрабатывать их таким образом, чтобы обеспечить плавность и прогнозируемость взаимодействия для игрока.
Это обработчики событий, используемые в этом примере, и события, которые они обрабатывают.
Обработчик событий | Description |
---|---|
OnActivated | Обрабатывает CoreApplicationView::Activated. Приложение игры было доставлено на передний план, поэтому основное окно активируется. |
OnDpiChanged | Обрабатывает графику::D isplay::D isplayInformation::D piChanged. DPI дисплея изменился, и игра корректирует свои ресурсы соответствующим образом.
Обратите внимание, чтокоординаты CoreWindow находятся в независимых от устройства пикселях (DIPs) для Direct2D. В результате необходимо уведомить Direct2D об изменении DPI, чтобы отобразить все 2D-ресурсы или примитивы правильно.
|
OnOrientationChanged | Обрабатывает графику::D isplay::D isplayInformation::OrientationChanged. Ориентация изменений отображения и отрисовки должна быть обновлена. |
OnDisplayContentsInvalidated | Обрабатывает графику::D isplay::D isplayInformation::D isplayContentsInvalidated. Для отображения требуется перерисовка, и игра должна быть отрисована снова. |
OnResuming | Обрабатывает CoreApplication::Resuming. Приложение игры восстанавливает игру из приостановленного состояния. |
OnSuspending | Обрабатывает CoreApplication::Suspending. Игровое приложение сохраняет состояние на диск. Оно имеет 5 секунд, чтобы сохранить состояние в хранилище. |
OnVisibilityChanged | Обрабатывает CoreWindow::VisibilityChanged. Приложение игры изменило видимость и стало видимым или невидимым другим приложением становится видимым. |
OnWindowActivationChanged | Обрабатывает CoreWindow::Activated. Главное окно приложения игры было деактивировано или активировано, поэтому он должен удалить фокус и приостановить игру или восстановить фокус. В обоих случаях наложение указывает, что игра приостановлена. |
OnWindowClosed | Обрабатывает CoreWindow::Closed. Игровое приложение закрывает главное окно и приостанавливает игру. |
OnWindowSizeChanged | Обрабатывает CoreWindow::SizeChanged. Приложение игры переопределяет графические ресурсы и наложение для изменения размера, а затем обновляет целевой объект отрисовки. |
Следующие шаги
В этой статье мы узнали, как общий поток игры управляется с помощью игровых состояний, и что игра состоит из нескольких различных машин состояния. Мы также узнали, как обновить пользовательский интерфейс и управлять обработчиками событий ключевых приложений. Теперь мы готовы изучить цикл отрисовки, игру и ее механику.
Вы можете пройти по остальным темам, которые документируйте эту игру в любом порядке.