게임 흐름 관리Game flow management

이제 게임에 몇 가지 이벤트 처리기가 등록된 창이 있으며 비동기적으로 자산을 로드합니다.The game now has a window, registered a couple event handlers, and loads assets asynchronously. 이 섹션은 게임 상태의 사용, 특정 키 게임 상태를 관리하는 방법 및 게임 엔진에 대한 업데이트 루프를 만드는 방법에 대해 설명합니다.This section explains about the use of game states, how to manage specific key game states, and how to create an update loop for the game engine. 그 다음 사용자 인터페이스 흐름에 대해 알아보고, 마지막으로 이벤트 처리기 및 UWP 게임에 필요한 이벤트에 대해 자세히 살펴봅니다.Then we'll learn about the user interface flow and finally, understand more about event handlers and events that are needed for a UWP game.

참고

이 샘플의 최신 게임 코드를 다운로드하지 않은 경우 Direct3D 게임 샘플로 이동합니다.If you haven't downloaded the latest game code for this sample, go to Direct3D game sample. 이 샘플은 UWP 기능 샘플의 큰 컬렉션의 일부입니다.This sample is part of a large collection of UWP feature samples. 샘플을 다운로드하는 방법에 대한 지침은 GitHub에서 UWP 샘플 가져오기를 참조하세요.For instructions on how to download the sample, see Get the UWP samples from GitHub.

게임 흐름을 관리하는 데 사용되는 게임 상태Game states used to manage game flow

게임 상태를 사용하여 게임의 흐름을 관리할 수 있습니다.We make use of game states to manage the flow of the game. 사용자는 언제든지 일시 중단 상태에서 UWP 게임 앱을 다시 시작할 수 있으므로 앱의 가능한 상태 수에 제한이 없습니다.Your game can have any number of possible states because a user can resume a UWP game app from a suspended state at any time.

이 게임 샘플은 시작할 때 다음 세 가지 상태 중 하나일 수 있습니다.For this game sample, it can be in one of the three states when it starts:

  • 게임 루프가 실행되고 있으며 한 수준에 있습니다.The game loop is running and is in the middle of a level.
  • 게임이 방금 완료되었으므로 게임 루프가 실행되고 있지 않습니다.The game loop is not running because a game had just been completed. (최고 점수가 설정됨)(The high score is set)
  • 시작된 게임이 없거나 게임이 수준 사이에 있습니다.No game has been started, or the game is between levels. (최고 점수는 0)(The high score is 0)

게임의 요구 사항에 따라 필요한 상태 수를 정의할 수 있습니다.You can have define the number of states required depending on your game's needs. 언제든지 UWP 게임을 종료할 수 있으며, 다시 시작할 때 플레이어는 게임이 재생 중지된 적이 없는 것처럼 동작하기를 기대한다는 것을 명심하세요.Again, be aware that your UWP game can be terminated at any time, and when it resumes, the player expects the game to behave as though they had never stopped playing.

게임 상태 초기화Game states initialization

게임 초기화에서 사용자는 앱 콜드 부팅만이 아니라 앱이 일시 중지되거나 종료된 후 다시 시작하는 것에 집중합니다.In game initialization, you are not just focused on cold starting the game but also restarting after it has been paused or terminated. 샘플 게임은 항상 게임 상태를 저장하여 앱이 계속 실행되고 있는 것처럼 표시되게 합니다.The sample game always saves the game state giving it the appearance that it has stayed running.

일시 중단 상태는 게임 플레이가 일시 중단되었지만 게임 리소스가 여전히 메모리에 있음을 나타냅니다.In a suspended state, game play is suspended but the resources of the game are still in memory.

마찬가지로, 다시 시작 이벤트는 샘플 게임이 마지막으로 일시 중단 또는 종료된 위치에서 다시 시작하도록 합니다.Likewise, the resume event is to ensure that the sample game picks up where it was last suspended or terminated. 샘플 게임을 종료한 후 다시 시작하면 게임이 정상적으로 시작된 다음 플레이어가 게임을 즉시 계속할 수 있도록 마지막으로 알려진 상태를 확인합니다.When the sample game restarts after termination, it starts up normally and then determines the last known state so the player can immediately continue playing.

상태에 따라 다른 옵션이 플레이어에게 제공됩니다.Depending on the state, different options are presented to the player.

  • 게임이 중간 수준에서 다시 시작되면 게임이 일시 중지된 것처럼 표시되며 오버레이에 계속 옵션이 제공됩니다.If the game resumes mid-level, it appears as paused, and the overlay presents a continue option.
  • 게임을 완료한 상태에서 게임을 다시 시작하면 최고 점수와 새 게임을 플레이하는 옵션이 표시됩니다.If the game resumes in a state where the game is completed, it displays the high scores and an option to play a new game.
  • 마지막으로, 수준이 시작되기 전에 게임이 다시 시작되면 오버레이에서 사용자에게 시작 옵션을 제공합니다.Lastly, if the game resumes before a level has started, the overlay presents a start option to the user.

게임 샘플은 일시 중단 이벤트 없이 처음 시작하는 콜드 부팅과 일시 중단 상태에서 다시 시작하는 시작을 구분하지 않습니다.The game sample doesn't distinguish whether the game is cold starting, launching for the first time without a suspend event, or resuming from a suspended state. 이 디자인은 모든 UWP 앱에 적합합니다.This is the proper design for any UWP app.

이 샘플에서 게임 상태 초기화는 GameMain::InitializeGameState에서 발생합니다.In this sample, initialization of the game states occurs in GameMain::InitializeGameState.

이 순서도는 흐름을 시각화하는 데 도움이 되며, 초기화 및 업데이트 관련 루프를 모두 다룹니다.This is a flowchart to help you visualize the flow, it covers both initialization and the update loop.

게임의 주 상태 시스템

GameMain::InitializeGameState 메서드GameMain::InitializeGameState method

InitializeGameState 메서드는 GameMain 클래스 개체가 App::Load 메서드에서 생성될 때 호출되는 GameMain 생성자 클래스에서 호출됩니다.The InitializeGameState method is called from the GameMain constructor class, which is called when the GameMain class object is created in the App::Load method.


GameMain::GameMain(...)
{
    m_deviceResources->RegisterDeviceNotify(this);
    ...

    create_task([this]()
    {
        ...

    }).then([this]()
    {
        // The finalize code needs to run in the same thread context
        // as the m_renderer object was created because the D3D device context
        // can ONLY be accessed on a single thread.
        m_renderer->FinalizeCreateGameDeviceResources();

        InitializeGameState(); //Initialization of game states occurs here.
        
        ...
    
    }, task_continuation_context::use_current()).then([this]()
    {
        ...
        
    }, task_continuation_context::use_current());
}


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_updateState = UpdateEngineState::WaitingForResources;
        m_pressResult = PressResultState::PlayLevel;
        SetGameInfoOverlay(GameInfoOverlayState::LevelStart);
        m_uiControl->SetAction(GameInfoOverlayCommand::PleaseWait);
    }
    m_uiControl->ShowGameInfoOverlay();
}

게임 엔진 업데이트Update game engine

App::Run 메서드에서 GameMain::Run을 호출합니다.In the App::Run method, it calls GameMain::Run. 샘플에서는 플레이어가 수행할 수 있는 모든 주요 작업을 처리하는 기본 상태 시스템을 이 메서드 내에 구현했습니다.Within this method, the sample has implemented a basic state machine for handling all the major actions the player can take. 이 상태 시스템의 최상위 수준에서는 게임 로드, 특정 레벨 플레이 또는 시스템 또는 플레이어에 의해 게임이 일시 중지된 후 레벨 진행을 처리합니다.The highest level of this state machine deals with loading a game, playing a specific level, or continuing a level after the game has been paused (by the system or the player).

게임 샘플에는 게임의 가능한 세 가지 주요 상태(UpdateEngineState)가 있습니다.In the game sample, there are 3 major states (UpdateEngineState) the game can be:

  1. 리소스 대기: 게임 루프가 순환되며 리소스(특히 그래픽 리소스)가 사용 가능할 때까지 전환할 수 없습니다.Waiting for resources: The game loop is cycling, unable to transition until resources (specifically graphics resources) are available. 리소스를 로드하는 비동기 작업이 완료되면 상태를 __ResourcesLoaded__로 업데이트합니다.When the async tasks for loading resources completes, it updates the state to ResourcesLoaded. 일반적으로 디스크, 게임 서버, 또는 클라우드 백 엔드에서 수준이 새 리소스를 로드할 때 발생합니다.This usually happens between levels when the level is loading new resources from disk, game server, or cloud backend. 게임 샘플에서 이 동작은 해당 시점에 샘플에 수준별 추가 리소스가 필요 없기 때문에 시뮬레이트됩니다.In the game sample, we simulate this behavior because the sample doesn't need any additional per-level resources at that time.
  2. 키를 눌러 기다리는: 게임 루프가 순환되며 특정 사용자 입력을 기다립니다.Waiting for press: The game loop is cycling, waiting for specific user input. 이 입력은 게임을 로드하거나, 수준을 시작하거나, 수준을 계속하는 플레이어 작업입니다.This input is a player action to load a game, start a level, or continue a level. 이 샘플 코드에서는 이러한 하위 상태를 PressResultState 열거형 값으로 나타냅니다.The sample code refers to these sub-states as PressResultState enumeration values.
  3. Dynamics: 게임 루프가 사용자 플레이로 실행되고 있습니다.In Dynamics: The game loop is running with the user playing. 사용자가 플레이하는 동안 게임은 전환할 수 있는 3가지 조건을 확인합니다.While the user is playing, the game checks for 3 conditions that it can transition on:
    • TimeExpired: 수준에 대해 설정된 시간 만료TimeExpired: expiration of the set time for a level
    • LevelComplete: 플레이어에 의해 수준 완료LevelComplete: completion of a level by the player
    • GameComplete: 플레이어에 의해 모든 수준 완료GameComplete: completion of all levels by the player

게임은 여러 작은 상태 시스템을 포함하는 상태 시스템입니다.Your game is simply a state machine containing multiple smaller state machines. 각 특정 상태는 매우 구체적인 특정 조건으로 정의되어야 합니다.For each specific state, it must be defined by a very specific criteria. 한 상태에서 다른 상태로 전환하는 방법은 개별 사용자 입력 또는 시스템 동작(예: 그래픽 리소스 로드)을 기반으로 해야 합니다.How it transitions from one state to another must be based on discrete user input or system actions (such as graphics resource loading). 게임을 계획할 때 사용자 또는 시스템이 수행할 수 있는 가능한 모든 작업을 다룰 수 있도록 전체 게임 흐름을 그리는 것을 고려하세요.While planning for your game, consider drawing out the entire game flow, to ensure that you've addressed all possible actions the user or the system can take. 게임은 매우 복잡할 수 있으며 그렇기 때문에 상태 시스템은 이러한 복잡성을 시각화하여 보다 관리하기 쉽게 만드는 강력한 도구입니다.Games can be very complicated so a state machine is a powerful tool to help visualize this complexity and make it more manageable.

아래에서 업데이트 루프에 대한 코드를 살펴보겠습니다.Let's take a look at the codes for the update loop below.

App::Update 메서드App::Update method

게임 엔진을 업데이트하는 데 사용된 상태 시스템의 구조The structure of the state machine used to update the game engine

void GameMain::Update()
{
    m_controller->Update(); //the controller instance has its own update loop.

    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
        {
            GameState runState = m_game->RunGame(); //when the player is playing, the work is handled by this Simple3DGame::RunGame method.
            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;
    }
}

사용자 인터페이스 업데이트Update user interface

플레이어가 시스템 상태에 대한 알림을 계속 받도록 하여 플레이어의 동작 및 게임을 정의하는 규칙에 따라 게임 상태를 변경할 수 있도록 해야 합니다.We need to keep the player apprised of the state of the system, and allow the game state to change depending on the player's actions and the rules that define the game. 이 게임 샘플을 포함하여 게임 대부분은 일반적으로 사용자 인터페이스(UI) 요소를 사용하여 플레이어에게 이 정보를 제공합니다.Many games, including this game sample, commonly use user interface (UI) elements to present this info to the player. UI에는 게임 상태 및 점수 또는 탄약, 또는 남은 가능성의 수와 같은 기타 재생 관련 정보가 포함됩니다.The UI contains representations of game state, and other play-specific info such as score, or ammo, or the number of chances remaining. UI는 주 그래픽 파이프라인과 별도로 렌더링되고 3D 투영 위에 배치되므로 오버레이라고도 합니다.UI is also called the overlay because it is rendered separately from the main graphics pipeline and placed on top the 3D projection. 일부 UI 정보도 게임 플레이 주요 영역을 벗어나지 않고 사용자가 이러한 정보를 얻을 수 있도록 표시 HUD(헤드업 디스플레이)로 제공됩니다.Some UI info is also presented as a heads-up display (HUD) to allow users to get these info without taking their eyes off the main gameplay area. 샘플 게임에서 이 오버레이는 Direct2D API를 사용하여 만듭니다.In the sample game, we create this overlay using the Direct2D APIs. XAML을 사용하여 이 오버레이를 만들 수도 있으며, 게임 샘플 확장에서 설명합니다.We can also create this overlay using XAML, which we discuss in Extending the game sample.

사용자 인터페이스에 대한 구성 요소는 다음과 같이 두 가지가 있습니다.There are two components to the user interface:

  • 점수 및 현재 게임 플레이 상태에 대한 정보를 포함하는 HUD.The HUD that contains the score and info about the current state of game play.
  • 게임의 일시 중지/일시 중단 상태 중 오버레이된 텍스트가 있는 검정색 사각형의 일시 중지 비트맵.The pause bitmap, which is a black rectangle with text overlaid during the paused/suspended state of the game. 게임 오버레이입니다.This is the game overlay. 게임 오버레이는 나중에 사용자 인터페이스 추가에서 살펴봅니다.We discuss it further in Adding a user interface.

당연히 오버레이에도 상태 시스템이 있습니다.Unsurprisingly, the overlay has a state machine too. 오버레이는 레빌 시작 또는 게임 종료 메시지를 표시할 수 있습니다.The overlay can display a level start or game over message. 기본적으로 게임이 일시 중지되거나 일시 중단되면 플레이어에게 표시할 게임 상태에 대한 정보를 출력하는 캔버스입니다.It is essentially a canvas to output any info about game state that we display to the player when the game is paused or suspended.

렌더링된 오버레이는 게임의 상태에 따라 6 화면 중 하나가 될 수 있습니다.Overlay rendered can be one of the six screens, depending on the state of the game:

  1. 게임 시작 시 화면을 로드하는 리소스Resources loading screen at the start of the game
  2. 게임 통계 플레이 화면Game stats play screen
  3. 수준 시작 메시지 화면Level start message screen
  4. 시간이 부족하지 않고 모든 수준이 완료되었을 때 게임 종료 화면Game over screen when all of the levels are completed without time running out
  5. 시간이 초과될 경우 게임 종료 화면Game over screen when time runs out
  6. 일시 중지 메뉴 화면Pause menu screen

게임의 그래픽 파이프라인에서 사용자 인터페이스를 분리하면 게임의 독립적인 그래픽 렌더링 엔진에서 작업을 수행하여 게임 코드의 복잡성을 현저하게 줄일 수 있습니다.Separating your user interface from your game's graphics pipeline allows you to work on it independent of the game's graphics rendering engine and decreases the complexity of your game's code significantly.

다음은 게임 샘플에서 오버레이의 상태 시스템을 구조화하는 방법입니다.Here's how the game sample structures the overlay's state machine.

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;
    }
}

이벤트 처리Events handling

샘플 코드에서는 App.cpp의 Initialize, SetWindowLoad의 특정 이벤트에 대해 많은 처리기를 등록했습니다.Our sample code registered a number of handlers for specific events in Initialize, SetWindow, and Load in the App.cpp. 이러한 이벤트는 게임 기술을 추가하거나 그래픽 개발을 시작하기 전에 작업해야 하는 중요한 이벤트입니다.These are important events that needs to work before we can add game mechanics or start graphics development. 이러한 이벤트는 적절한 UWP 앱 환경에 기본적인 요소입니다.These events are fundamental to a proper UWP app experience. UWP 앱은 언제든지 활성화, 비활성화, 크기 조정, 스냅, 스냅 취소, 일시 중단 또는 다시 시작할 수 있으므로 게임에서 최대한 빨리 해당 이벤트를 등록하고 환경을 원활하며 플레이어가 예측할 수 있게 유지하는 방식으로 처리해야 합니다.Because a UWP app can be activated, deactivated, resized, snapped, unsnapped, suspended, or resumed at any time, the game must register for these events as soon as it can, and handle them in a way that keeps the experience smooth and predictable for the player.

다음은 샘플에서 사용되는 이벤트 처리기와 해당 처리기에서 처리하는 이벤트입니다.These are the event handlers used in this sample and the events they handle.

이벤트 처리기Event handler 설명Description
OnActivatedOnActivated CoreApplicationView::Activated를 처리합니다.Handles CoreApplicationView::Activated. 게임 앱이 전경으로 전환되었음으로 주 창이 활성화됩니다.The game app has been brought to the foreground, so the main window is activated.
OnDpiChangedOnDpiChanged Graphics::Display::DisplayInformation::DpiChanged를 처리합니다.Handles Graphics::Display::DisplayInformation::DpiChanged. 디스플레이의 DPI가 변경되었으며 게임이 해당 리소스를 적절하게 조정합니다.The DPI of the display has changed and the game adjusts its resources accordingly.
참고 CoreWindow 좌표가 Dip (장치 독립적 픽셀)에 대 한 Direct2D합니다. Note CoreWindow coordinates are in DIPs (Device Independent Pixels) for Direct2D. 따라서 2D 자산이나 기본 요소를 올바르게 표시하려면 Direct2D에 DPI 변경을 알려야 합니다.As a result, you must notify Direct2D of the change in DPI to display any 2D assets or primitives correctly.
OnOrientationChangedOnOrientationChanged Graphics::Display::DisplayInformation::OrientationChanged를 처리합니다.Handles Graphics::Display::DisplayInformation::OrientationChanged. 디스플레이 방향이 변경되며 렌더링을 업데이트해야 합니다.The orientation of the display changes and rendering needs to be updated.
OnDisplayContentsInvalidatedOnDisplayContentsInvalidated Graphics::Display::DisplayInformation::DisplayContentsInvalidated를 처리합니다.Handles Graphics::Display::DisplayInformation::DisplayContentsInvalidated. 디스플레이 다시 그리기가 필요하며 사용자의 게임을 다시 렌더링해야 합니다.The display requires redrawing and your game needs to be rendered again.
OnResumingOnResuming CoreApplication::Resuming을 처리합니다.Handles CoreApplication::Resuming. 게임 앱이 일시 중단 상태에서 게임을 복원합니다.The game app restores the game from a suspended state.
OnSuspendingOnSuspending CoreApplication::Suspending을 처리합니다.Handles CoreApplication::Suspending. 게임 앱에서 해당 상태를 디스크에 저장합니다.The game app saves its state to disk. 상태를 저장소에 저장하려면 5초가 필요합니다.It has 5 seconds to save state to storage.
OnVisibilityChangedOnVisibilityChanged CoreWindow::VisibilityChanged를 처리합니다.Handles CoreWindow::VisibilityChanged. 게임 앱의 가시성이 변경되어 표시되는 것으로 변환되거나 표시되지 않고 다른 앱 표시로 전환되었습니다.The game app has changed visibility, and has either become visible or been made invisible by another app becoming visible.
OnWindowActivationChangedOnWindowActivationChanged CoreWindow::Activated를 처리합니다.Handles CoreWindow::Activated. 게임 앱의 주 창이 비활성화되거나 활성화되어 포커스를 제거하고 게임을 일시 중지하거나 포커스를 다시 얻어야 합니다.The game app's main window has been deactivated or activated, so it must remove focus and pause the game, or regain focus. 두 경우 모두 오버레이에는 게임이 일시 중지된 것으로 표시됩니다.In both cases, the overlay indicates that the game is paused.
OnWindowClosedOnWindowClosed CoreWindow::Closed를 처리합니다.Handles CoreWindow::Closed. 게임 앱에서 주 창을 닫고 게임을 일시 중단합니다.The game app closes the main window and suspends the game.
OnWindowSizeChangedOnWindowSizeChanged CoreWindow::SizeChanged를 처리합니다.Handles CoreWindow::SizeChanged. 게임 앱에서 크기 변경을 수용하도록 그래픽 리소스 및 오버레이를 다시 할당한 다음 렌더링 대상을 업데이트합니다.The game app reallocates the graphics resources and overlay to accommodate the size change, and then updates the render target.

다음 단계Next steps

이 항목에서는 게임 상태를 사용하여 전체 게임 흐름을 관리하는 방법과 여러 다른 상태 시스템으로 구성된 게임에 대해 알아보았습니다.In this topic, we've covered how the overall game flow is managed using game states and that a game is made up of multiple different state machines. 또한 UI를 업데이트하고 주요 앱 이벤트 처리기를 관리하는 방법에 대해 학습했습니다.We've also learnt about how to update the UI and manage key app event handlers. 이제 루프, 게임 및 방법을 렌더링하는 것에 자세히 알아볼 준비가 되었습니다.Now we are ready to dive into the rendering loop, the game, and its mechanics.

이 게임을 구성하는 다른 구성 요소를 원하는 순서대로 살펴볼 수 있습니다.You can go through the other components that make up this game in any order: