ゲームのフロー管理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.

このゲーム サンプルの場合、ゲームの開始時に、次の 3 つのいずれかの状態になります。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)
  • ゲームが開始されていないか、ゲームが 2 つのレベルの中間にある状態。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.

  • ゲームは、2 つのレベルの中間から再開された場合、一時停止しているように見え、オーバーレイで続行オプションが表示されます。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) として次の 3 つがあります。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. これは通常、2 つのレベルの中間で発生します。レベルの中間では、新しいリソースをディスク、ゲーム サーバー、クラウド バックエンドから読み込みます。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.

ユーザー インターフェイスには次の 2 つのコンポーネントがあります。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 の InitializeSetWindowLoad に登録されています。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: