Определение основного игрового объекта
Примечание.
Этот раздел является частью серии руководств по созданию простой игры универсальная платформа Windows (UWP) с помощью DirectX. Эта ссылка задает контекст для ряда.
После того как вы изложили базовую платформу примера игры и реализовали машину состояния, которая обрабатывает поведение пользователя высокого уровня и системы, вы захотите проверить правила и механики, которые превратят образец игры в игру. Рассмотрим подробные сведения о главном объекте игры образца и о том, как перевести правила игры в взаимодействие с игровым миром.
Задачи
- Узнайте, как применять основные методы разработки для реализации правил игры и механики для игры UWP DirectX.
Основной игровой объект
В примере игры Simple3DGameDX Simple3DGame — это основной класс объектов игры. Экземпляр Simple3DGame создается косвенно с помощью метода App::Load .
Ниже приведены некоторые функции класса Simple3DGame .
- Содержит реализацию логики игрового процесса.
- Содержит методы, которые передают эти сведения.
- Изменяет состояние игры на компьютер состояния, определенный в платформе приложений.
- Изменения в состоянии игры из приложения на сам объект игры.
- Сведения об обновлении пользовательского интерфейса игры (наложение и отображение головных окон), анимации и физики (динамики).
Примечание.
Обновление графики обрабатывается классом GameRenderer , который содержит методы для получения и использования ресурсов графического устройства, используемых игрой. Дополнительные сведения см. в разделе "Инфраструктура отрисовки" для отрисовки.
- Служит контейнером для данных, определяющих игровой сеанс, уровень или время существования, в зависимости от того, как вы определяете игру на высоком уровне. В этом случае данные о состоянии игры в течение всего времени существования игры инициализированы один раз, когда пользователь запускает игру.
Сведения о методах и данных, определенных этим классом, см. в разделе "Класс Simple3DGame" ниже.
Инициализация и запуск игры
Когда игрок запускает игру, объект игры должен инициализировать состояние, создать и добавить наложение, задать переменные, отслеживающие производительность игрока, и создать экземпляры объектов, которые будут использоваться для создания уровней. В этом примере это делается при создании экземпляра GameMain в App::Load.
Объект игры с типом Simple3DGame создается в конструкторе GameMain::GameMain . Затем он инициализируется с помощью метода Simple3DGame::Initialize во время gameMain::ConstructInBackground fire-and-forget coroutine, который вызывается из GameMain::GameMain.
Метод Simple3DGame::Initialize
Пример игры настраивает эти компоненты в объекте игры.
- Создается новый объект воспроизведения звука.
- Создаются массивы графических примитивов игры, включая массивы для примитивов уровня, аммо и препятствий.
- Расположение для сохранения данных о состоянии игры создается, называется Game и помещается в расположение хранилища данных приложения, указанное ApplicationData::Current.
- Создается таймер игры и начальная наложенная наложенная карта.
- Новая камера создается с определенным набором параметров представления и проекции.
- Устройство ввода (контроллер) устанавливается на тот же начальный шаг и рывок, что и камера, поэтому проигрыватель имеет соответствие от 1 до 1 между начальной позицией управления и положением камеры.
- Объект проигрывателя создается и устанавливается для активного. Мы используем объект sphere для обнаружения близости игрока к стенам и препятствиям и держать камеру от размещения в положении, которое может нарушить погружение.
- Создается примитив мира игры.
- Создаются цилиндрические препятствия.
- Целевые объекты (объекты распознавания лиц ) создаются и нумеруются.
- Создаются сферы аммо.
- Создаются уровни.
- Высокая оценка загружается.
- Загружается любое предыдущее сохраненное состояние игры.
Игра теперь имеет экземпляры всех ключевых компонентов — мира, игрока, препятствий, целей и аммосфер. Он также имеет экземпляры уровней, которые представляют конфигурации всех перечисленных выше компонентов и их поведения для каждого конкретного уровня. Теперь давайте посмотрим, как игра создает уровни.
Создание и загрузка уровней игры
Большая часть тяжелого подъема для строительства уровня выполняется в файлах, найденных в Level[N].h/.cpp
папке GameLevels примера решения. Поскольку он фокусируется на очень конкретной реализации, мы не будем охватывать их здесь. Важно, чтобы код для каждого уровня выполнялся как отдельный объект Level[N] . Если вы хотите расширить игру, можно создать объект Level[N] , который принимает назначенное число в качестве параметра и случайным образом помещает препятствия и цели. Кроме того, можно использовать данные конфигурации уровня загрузки из файла ресурсов или даже в Интернете.
Определение игрового процесса
На этом этапе у нас есть все компоненты, необходимые для разработки игры. Уровни были созданы в памяти из примитивов и готовы к взаимодействию с игроком.
Лучшие игры мгновенно реагируют на входные данные игрока и предоставляют немедленную обратную связь. Это верно для любого типа игры, от twitch-action, в режиме реального времени первый человек стрелять до задумчивых, повернутых стратегий игры.
Метод Simple3DGame::RunGame
В то время как уровень игры выполняется, игра находится в состоянии Dynamics .
GameMain::Update — это основной цикл обновления, который обновляет состояние приложения один раз на кадр, как показано ниже. Цикл обновления вызывает метод Simple3DGame::RunGame для обработки работы, если игра находится в состоянии Dynamics .
// Updates the application state once per frame.
void GameMain::Update()
{
// The controller object has its own update loop.
m_controller->Update();
switch (m_updateState)
{
...
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)
{
...
Simple3DGame::RunGame обрабатывает набор данных, определяющий текущее состояние игры для текущей итерации цикла игры.
Ниже приведена логика потока игры в Simple3DGame::RunGame.
- Метод обновляет таймер, который отсчитывает секунды до завершения уровня, и проверяет, истекло ли время уровня. Это одна из правил игры - когда время истекает, если не все цели были застрелены, то это игра над.
- Если время истекло, метод задает состояние игры TimeExpired и возвращает метод Update в предыдущем коде.
- Если время остается, контроллер перемещения будет опрашивать для обновления положения камеры; в частности, обновление до угла представления нормального проецирования с плоскости камеры (где игрок смотрит), и расстояние, которое перемещается с момента последнего опроса контроллера.
- Камера обновляется на основе новых данных с контроллера перемещения.
- Обновляются динамики или анимации и поведения объектов в игровом мире независимо от элемента управления игроком. В этом примере игры вызывается метод Simple3DGame::UpdateDynamics для обновления движения сфер аммо, анимации препятствий столба и движения целевых объектов. Дополнительные сведения см. в разделе "Обновление игрового мира".
- Метод проверяет, выполнены ли критерии успешного завершения уровня. Если это так, он завершает оценку для уровня и проверяет, является ли это последним уровнем (из 6). Если это последний уровень, метод возвращает состояние игры GameState::GameComplete ; в противном случае возвращается состояние игры GameState::LevelComplete .
- Если уровень не завершен, метод задает состояние игры в GameState::Active и возвращается.
Обновление игрового мира
В этом примере при запуске игры метод Simple3DGame::UpdateDynamics вызывается из метода Simple3DGame::RunGame (который вызывается из GameMain::Update) для обновления объектов, отрисованных в игровой сцене.
Цикл, такой как UpdateDynamics , вызывает любые методы, которые используются для настройки игрового мира в движении, независимо от входных данных игрока, чтобы создать иммерсивный игровой интерфейс и сделать уровень живым. Это включает в себя графику, которая должна быть отрисовка, и выполнение циклов анимации для обеспечения динамического мира даже при отсутствии входных данных проигрывателя. В вашей игре, которая может включать деревья, продвигающиеся в ветер, волны крестинг вдоль береговой линии, машинное курение, и чужие монстры растяжения и перемещения вокруг. Он также охватывает взаимодействие между объектами, включая столкновения между сферой игрока и миром, или между боеприпасами и препятствиями и целями.
За исключением случаев, когда игра специально приостановлена, цикл игры должен продолжать обновлять игровой мир; зависит ли это от логики игры, физических алгоритмов или простого случайного.
В примере игры этот принцип называется динамикой, и он охватывает подъем и падение препятствий столба, а также движение и физическое поведение сфер аммо, как они уволены и в движении.
Метод Simple3DGame::UpdateDynamics
Этот метод относится к этим четырем наборам вычислений.
- Позиции уволенных сфер боеприпасов в мире.
- Анимация препятствий столба.
- Пересечение игроков и границ мира.
- Столкновения сфер аммо с препятствиями, целевыми объектами, другими сферами аммо и миром.
Анимация препятствий происходит в цикле, определенном в файлах исходного кода Animate.h/.cpp . Поведение ammo и любых столкновений определяется упрощенными алгоритмами физики, предоставляемыми в коде и параметризованным набором глобальных констант для игрового мира, включая гравитацию и свойства материала. Это все вычисляется в пространстве координат игрового мира.
Просмотр потока
Теперь, когда мы обновили все объекты в сцене и вычислили любые столкновения, нам нужно использовать эту информацию для рисования соответствующих визуальных изменений.
После завершения текущей итерации цикла игры GameMain::Update образец немедленно вызывает GameRenderer::Render , чтобы принять обновленные данные объекта и создать новую сцену для представления игроку, как показано ниже.
void GameMain::Run()
{
while (!m_windowClosed)
{
if (m_visible)
{
switch (m_updateState)
{
case UpdateEngineState::Deactivated:
case UpdateEngineState::TooSmall:
...
// Otherwise, fall through and do normal processing to perform rendering.
default:
CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(
CoreProcessEventsOption::ProcessAllIfPresent);
// GameMain::Update calls Simple3DGame::RunGame. If game is in Dynamics
// state, uses Simple3DGame::UpdateDynamics to update game world.
Update();
// Render is called immediately after the Update loop.
m_renderer->Render();
m_deviceResources->Present();
m_renderNeeded = false;
}
}
else
{
CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(
CoreProcessEventsOption::ProcessOneAndAllPending);
}
}
m_game->OnSuspending(); // Exiting due to window close, so save state.
}
Отрисовка графики игрового мира
Рекомендуется, чтобы графики в обновлении игры часто, в идеале точно так же часто, как основной игровой цикл итерирует. По мере итерации цикла состояние игрового мира обновляется с вводом игрока или без него. Это позволяет плавно отображать вычисляемые анимации и поведение. Представьте себе, если у нас была простая сцена воды, которая перемещалась только когда игрок нажимал кнопку. Это было бы не реалистично; хорошая игра выглядит гладкой и жидкости все время.
Помните цикл примера игры, как показано выше в GameMain::Run. Если главное окно игры отображается и не отключается или не отключается, игра продолжает обновляться и отображать результаты этого обновления. Метод GameRenderer::Render мы рассмотрим следующее представление этого состояния. Это выполняется сразу после вызова GameMain::Update, который включает Simple3DGame::RunGame для обновления состояний, как описано в предыдущем разделе.
GameRenderer::Render рисует проекцию трехмерного мира, а затем рисует наложение Direct2D поверх него. По завершении она представляет конечную цепочку буферов с объединенными буферами для отображения.
Примечание.
Существует два состояния для наложения в примере игры Direct2D, где игра отображает сведения о игре, содержащие растровое изображение для меню приостановки, и один из которых игра отображает перекрестия вместе с прямоугольниками для контроллера перемещения сенсорного экрана. Текст оценки рисуется в обоих состояниях. Дополнительные сведения см. в разделе "Инфраструктура отрисовки" для отрисовки.
Метод GameRenderer::Render
void GameRenderer::Render()
{
bool stereoEnabled{ m_deviceResources->GetStereoState() };
auto d3dContext{ m_deviceResources->GetD3DDeviceContext() };
auto d2dContext{ m_deviceResources->GetD2DDeviceContext() };
...
if (m_game != nullptr && m_gameResourcesLoaded && m_levelResourcesLoaded)
{
// This section is only used after the game state has been initialized and all device
// resources needed for the game have been created and associated with the game objects.
...
for (auto&& object : m_game->RenderObjects())
{
object->Render(d3dContext, m_constantBufferChangesEveryPrim.get());
}
}
d3dContext->BeginEventInt(L"D2D BeginDraw", 1);
d2dContext->BeginDraw();
// To handle the swapchain being pre-rotated, set the D2D transformation to include it.
d2dContext->SetTransform(m_deviceResources->GetOrientationTransform2D());
if (m_game != nullptr && m_gameResourcesLoaded)
{
// This is only used after the game state has been initialized.
m_gameHud.Render(m_game);
}
if (m_gameInfoOverlay.Visible())
{
d2dContext->DrawBitmap(
m_gameInfoOverlay.Bitmap(),
m_gameInfoOverlayRect
);
}
...
}
}
Класс Simple3DGame
Это методы и элементы данных, определенные классом Simple3DGame .
Функции элементов
Общедоступные функции-члены, определенные Simple3DGame , включают следующие функции.
- Инициализация. Задает начальные значения глобальных переменных и инициализирует объекты игры. Это рассматривается в разделе инициализации и запуска игры .
- LoadGame. Инициализирует новый уровень и начинает загрузку.
- LoadLevelAsync. Корутин, который инициализирует уровень, а затем вызывает другую корутину на отрисовщике для загрузки ресурсов на уровне устройства. Этот метод выполняется в отдельном потоке; В результате из этого потока можно вызывать только методы ID3D11Device (а не методы ID3D11DeviceContext). Все методы контекста устройства вызываются в методе FinalizeLoadLevel . Если вы не знакомы с асинхронным программированием, см . статью параллелизм и асинхронные операции с C++/WinRT.
- ЗавершениеloadLevel. Завершает любую работу для загрузки уровня, которую необходимо выполнить в основном потоке. Сюда входят все вызовы методов контекста устройства Direct3D 11 (ID3D11DeviceContext).
- StartLevel. Запускает игровой процесс для нового уровня.
- PauseGame. Приостанавливает игру.
- RunGame. Выполняет итерацию цикла игры. Он вызывается из App::Update один раз при каждом итерации цикла игры, если состояние игры является активным.
- OnSuspending и OnResuming. Приостановка и возобновление звука игры соответственно.
Ниже приведены частные функции-члены.
- LoadSavedState и SaveState. Загрузка и сохранение текущего состояния игры соответственно.
- LoadHighScore и SaveHighScore. Загрузка и сохранение высокой оценки в играх соответственно.
- ИнициализацияAmmo. Сбрасывает состояние каждого объекта сферы, используемого в качестве боеприпасов, обратно в исходное состояние для начала каждого раунда.
- UpdateDynamics. Это важный метод, так как он обновляет все игровые объекты на основе консервированных процедур анимации, физики и управления входными данными. Это сердце интерактивности, определяющей игру. Это рассматривается в разделе "Обновление игрового мира ".
Другие общедоступные методы — это метод доступа к свойствам, возвращающий сведения о игровом процессе и наложении в платформу приложений для отображения.
Элементы данных
Эти объекты обновляются при выполнении цикла игры.
- Объект MoveLookController . Представляет входные данные проигрывателя. Дополнительные сведения см. в разделе "Добавление элементов управления".
- Объект GameRenderer . Представляет отрисовщик Direct3D 11, который обрабатывает все объекты, относящиеся к устройству, и их отрисовку. Дополнительные сведения см. в разделе "Инфраструктура отрисовки i".
- Звуковой объект. Управляет воспроизведением звука для игры. Дополнительные сведения см. в разделе "Добавление звука".
Остальные переменные игры содержат списки примитивов и их соответствующие объемы в игре, а также игры играют определенные данные и ограничения.
Следующие шаги
Мы еще не поговорим о фактическом обработчике отрисовки — о том, как вызовы методов отрисовки на обновленных примитивах превратились в пиксели на экране. Эти аспекты рассматриваются в двух частях: платформа отрисовки I: введение в отрисовку и отрисовку платформы II: отрисовка игры. Если вы больше заинтересованы в том, как игрок управляет обновлением состояния игры, см. статью "Добавление элементов управления".