Поделиться через


Определение основного игрового объекта

Примечание.

Этот раздел является частью серии руководств по созданию простой игры универсальная платформа 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: отрисовка игры. Если вы больше заинтересованы в том, как игрок управляет обновлением состояния игры, см. статью "Добавление элементов управления".