コントロールの追加Add controls

[Windows 10 の UWP アプリ向けに更新。[ Updated for UWP apps on Windows 10. Windows 8.x の記事については、アーカイブをご覧ください。]For Windows 8.x articles, see the archive ]

優れた ユニバーサル Windows プラットフォーム (UWP) ゲームは、幅広いインターフェイスをサポートしています。A good Universal Windows Platform (UWP) game supports a wide variety of interfaces. 潜在的なプレイヤーが持っているのは、Windows 10 搭載で物理的なボタンのないタブレット、Xbox コントローラー付属の PC、または高性能マウス/ゲーム キーボード付属の最新デスクトップ ゲーム機かもしれません。A potential player might have Windows 10 on a tablet with no physical buttons, a PC with an Xbox controller attached, or the latest desktop gaming rig with a high-performance mouse and gaming keyboard. このゲームでは、コントロールは MoveLookController クラスで実装されます。In our game the controls are implemented in the MoveLookController class. このクラスは、3 種類のすべての入力 (マウスとキーボード、タッチ、ゲームパッド) を 1 つのコントローラーのに集約します。This class aggregates all three types of input (mouse and keyboard, touch, and gamepad) into a single controller. 最終的には、一人称視点のシューティング ゲームで使用するジャンル標準のムーブ/ルック コントロールが、複数のデバイスで利用できるようになります。The end result is a first-person shooter that uses genre standard move-look controls that work with multiple devices.

注意

コントロールの詳細ついては、「ゲームのムーブ/ルック コントロール」と「ゲームのタッチ コントロール」を参照してください。For more info about controls, see Move-look controls for games and Touch controls for games.

目標Objective

この時点で、レンダリングするゲームは完成していますが、プレイヤーが動き回ったり、ターゲットを撃ったりすることはできません。At this point we have a game that renders, but we can't move our player around or shoot the targets. ここでは、UWP DirectX ゲームで次の種類の入力について、一人称視点のシューティング ゲームのムーブ/ルック コントロールを実装する方法を見てみましょう。We'll take a look at how our game implements first person shooter move-look controls for the following types of input in our UWP DirectX game.

  • マウスとキーボードMouse and keyboard
  • タッチTouch
  • ゲームパッドGamepad

注意

このサンプルの最新ゲーム コードをダウンロードしていない場合は、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.

コントロールの共通の動作Common control behaviors

タッチ コントロールとマウス/キーボード コントロールのコア実装は、よく似ています。Touch controls and mouse/keyboard controls have a very similar core implementation. UWP アプリでは、ポインターは画面上の単なる点です。In a UWP app, a pointer is simply a point on the screen. これは、マウスをスライドするか、タッチ スクリーンで指をスライドすることで動かせます。You can move it by sliding the mouse or sliding your finger on the touch screen. このため、単一のイベント セットを登録でき、プレイヤーがポインターを動かして押すのにマウスとタッチ スクリーンのどちらを使っているかを気にする必要はありません。As a result, you can register for a single set of events, and not worry about whether the player is using a mouse or a touch screen to move and press the pointer.

このゲーム サンプルの MoveLookController クラスを初期化すると、ポインターに固有の 4 つのイベントとマウスに固有の 1 つのイベントが登録されます。When the MoveLookController class in the game sample is initialized, it registers for four pointer-specific events and one mouse-specific event:

イベントEvent 説明Description
CoreWindow::PointerPressedCoreWindow::PointerPressed マウスの左または右ボタンが押された (そして押され続けた) か、タッチ画面がタッチされました。The left or right mouse button was pressed (and held), or the touch surface was touched.
CoreWindow::PointerMovedCoreWindow::PointerMoved マウスが動かされたか、タッチ画面でドラッグ操作が行われました。The mouse moved, or a drag action was made on the touch surface.
CoreWindow::PointerReleasedCoreWindow::PointerReleased マウスの左ボタンが離されたか、タッチ画面に触れているオブジェクトが離されました。The left mouse button was released, or the object contacting the touch surface was lifted.
CoreWindow::PointerExitedCoreWindow::PointerExited ポインターがメイン ウィンドウの外に動かされました。The pointer moved out of the main window.
Windows::Devices::Input::MouseMovedWindows::Devices::Input::MouseMoved マウスが一定の距離動かされました。The mouse moved a certain distance. 現在の X-Y 位置ではなく、マウス移動のデルタ値にのみ注目します。Be aware that we are only interested in mouse movement delta values, and not the current X-Y position.

これらのイベント ハンドラーは、アプリケーション ウィンドウ内で MoveLookController が初期化されるとすぐに、ユーザー入力の待機を開始するように設定されています。These event handlers are set to start listening for user input as soon as the MoveLookController is initialized in the application window.

void MoveLookController::InitWindow(_In_ CoreWindow^ window)
{
    ResetState();

    window->PointerPressed +=
        ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerPressed);

    window->PointerMoved +=
        ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerMoved);

    window->PointerReleased +=
        ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerReleased);

    window->PointerExited +=
        ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerExited);

    // There is a separate handler for mouse only relative mouse movement events.
    MouseDevice::GetForCurrentView()->MouseMoved +=
        ref new TypedEventHandler<MouseDevice^, MouseEventArgs^>(this, &MoveLookController::OnMouseMoved);
    //
    // ...
    //
}

InitWindow のコード一式は GitHub で確認できます。Complete code for InitWindow can be seen on GitHub.

ゲームがいつ特定の入力を待機する必要があるかを判断するために、MoveLookController クラスには、コントローラーの種類に関係なく、コントローラーに固有の次の 3 つの状態があります。To determine when the game should be listening for certain input, the MoveLookController class has three controller-specific states, regardless of the controller type:

状態State 説明Description
NoneNone これは、コントローラーの初期化された状態です。This is the initialized state for the controller. ゲームではコントローラーの入力を予期していないため、すべての入力は無視されます。All input is ignored since the game is not anticipating any controller input.
WaitForInputWaitForInput コントローラーは、プレイヤーが、マウスの左クリック、タッチ イベント、ゲームパッドのメニュー ボタンのいずれかを使用して、ゲームからのメッセージを確認するのを待っています。The controller is waiting for the player to acknowledge a message from the game by either using a left mouse click, a touch event, ot the menu button on a gamepad.
ActiveActive コントローラーはアクティブなゲーム プレイ モードです。The controller is in active game play mode.

WaitForInput 状態とゲームの一時停止WaitForInput state and pausing the game

ゲームが一時停止されると、ゲームは WaitForInput 状態になります。The game enters the WaitForInput state when the game has been paused. これは、プレイヤーがゲームのメイン ウィンドウの外にポインターを動かすか、一時停止ボタン (P キーまたはゲームパッドのスタート ボタン) を押したときに発生します。This happens when the player moves the pointer outside the main window of the game, or presses the pause button (the P key or the gamepad Start button). MoveLookController は、この押し操作を登録し、IsPauseRequested メソッドを呼び出すときにゲーム ループに通知します。The MoveLookController registers the press, and informs the game loop when it calls the IsPauseRequested method. その時点で、IsPauseRequestedtrue を返すと、ゲーム ループは MoveLookControllerWaitForPress を呼び出して、コントローラーを WaitForInput 状態にします。At that point if IsPauseRequested returns true, the game loop then calls WaitForPress on the MoveLookController to move the controller into the WaitForInput state.

WaitForInput 状態になると、ゲームは、Active状態 に戻るまで、ほぼすべてのゲームプレイ入力イベントの処理を停止します。Once in the WaitForInput state, the game stops processing almost all gameplay input events until it returns to the Active state. 一時停止ボタンは例外で、このボタンを押すと、ゲームはアクティブ状態に戻ります。The exception is the pause button, with a press of this causing the game to go back to the active state. 一時停止ボタン以外の方法で、ゲームを Active 状態に戻すには、プレイヤーはメニュー項目を選択する必要があります。Other than the pause button, in order for the game to go back to the the Active state the player needs to select a menu item.

Active 状態The Active state

Active 状態では、MoveLookController インスタンスは、有効になっているすべての入力デバイスからの入力イベントを処理し、プレイヤーの意図を解釈しています。During the Active state, the MoveLookController instance is processing events from all enabled input devices and interpreting the player's intentions. この結果、Update がゲーム ループから呼び出された後に、プレイヤーのビューの速度とルック方向を更新し、更新データをゲームと共有します。As a result, it updates the velocity and look direction of the player's view and shares the updated data with the game after Update is called from the game loop.

すべてのポインター入力は、Active 状態で追跡され、ポインターの操作に応じて異なるポインター ID が設定されます。All pointer input is tracked in the Active state, with different pointer IDs corresponding to different pointer actions. PointerPressed イベントが受け取られると、MoveLookController は、ウィンドウで作成されたポインターの ID 値を取得します。When a PointerPressed event is received, the MoveLookController obtains the pointer ID value created by the window. ポインター ID は、特定の種類の入力を表します。The pointer ID represents a specific type of input. たとえば、マルチタッチ デバイスでは、複数の異なるアクティブ入力が同時に行われる場合があります。For example, on a multi-touch device, there may be several different active inputs at the same time. ID は、プレイヤーが使っている入力を追跡するために使われます。The IDs are used to keep track of which input the player is using. タッチ スクリーンのムーブ四角形内にイベントがある場合、ポインター ID が割り当てられ、ムーブ四角形内のポインター イベントが追跡されます。If one event is in the move rectangle of the touch screen, a pointer ID is assigned to track any pointer events in the move rectangle. ファイア四角形内の他のポインター イベントは、別のポインター ID で別途追跡されます。Other pointer events in the fire rectangle are tracked separately, with a separate pointer ID.

注意

マウスおよびゲームパッドの右サムスティックからの入力には、別途処理される別の ID もあります。Input from the mouse and right thumbstick of a gamepad also have IDs that are handled separately.

ポインター イベントをゲームの特定の操作にマップした後は、MoveLookController オブジェクトとゲームのメイン ループで共有されているデータを更新します。After the pointer events have been mapped to a specific game action, it's time to update the data the MoveLookController object shares with the main game loop.

このゲーム サンプルの Update メソッドは、呼び出されると、入力を処理し、速度とルック方向の変数 (m_velocitym_lookdirection) を更新します。この後、ゲーム ループは、Velocity および LookDirection パブリック メソッドを呼び出すことで、これらの変数を取得します。When called, the Update method in the game sample processes the input and updates the velocity and look direction variables (m_velocity and m_lookdirection), which the game loop then retrieves by calling the public Velocity and LookDirection methods.

注意

Update メソッドの詳細については、このページで後で説明します。More details about the Update method can be seen later on this page.

ゲーム ループは、MoveLookController インスタンスの IsFiring メソッドを呼び出すことで、プレイヤーが弾を撃っているかどうかをテストできます。The game loop can test to see if the player is firing by calling the IsFiring method on the MoveLookController instance. MoveLookController は、プレイヤーが 3 種類の入力のいずれかでファイア ボタンを押したかどうかを確認します。The MoveLookController checks to see if the player has pressed the fire button on one of the three input types.

bool MoveLookController::IsFiring()
{
    if (m_state == MoveLookControllerState::Active)
    {
        if (m_autoFire)
        {
            return (m_fireInUse || (m_mouseInUse && m_mouseLeftInUse) || PollingFireInUse());
        }
        else
        {
            if (m_firePressed)
            {
                m_firePressed = false;
                return true;
            }
        }
    }
    return false;
}

次は、3 種類のコントロールのそれぞれの実装について少し詳しく説明します。Now, let's look at the implementation of each of the three control types in a little more detail.

相対マウス コントロールの追加Adding relative mouse controls

マウス移動が検出された場合は、その移動を使ってカメラの新しいピッチとヨーを特定します。If mouse movement is detected, we want to use that movement to determine the new pitch and yaw of the camera. そのためには、相対マウス コントロールを実装します。相対マウス コントロールでは、動作の絶対 x-y ピクセル座標を記録するのではなく、マウスが移動した相対距離 (移動の開始から停止までのデルタ) を処理します。We do that by implementing relative mouse controls, where we handle the relative distance the mouse has moved—the delta between the start of the movement and the stop—as opposed to recording the absolute x-y pixel coordinates of the motion.

これを行うには、MouseMoved イベントによって返される Windows::Device::Input::MouseEventArgs::MouseDelta 引数オブジェクトの MouseDelta::X フィールドと MouseDelta::Y フィールドを調べて、X (横方向の動作) と Y (縦方向の動作) の座標の変化を取得します。To do that, we obtain the changes in the X (the horizontal motion) and the Y (the vertical motion) coordinates by examining the MouseDelta::X and MouseDelta::Y fields on the Windows::Device::Input::MouseEventArgs::MouseDelta argument object returned by the MouseMoved event.

void MoveLookController::OnMouseMoved(
    _In_ MouseDevice^ /* mouseDevice */,
    _In_ MouseEventArgs^ args
    )
{
    // Handle Mouse Input via dedicated relative movement handler.

    switch (m_state)
    {
    case MoveLookControllerState::Active:
        XMFLOAT2 mouseDelta;
        mouseDelta.x = static_cast<float>(args->MouseDelta.X);
        mouseDelta.y = static_cast<float>(args->MouseDelta.Y);

        XMFLOAT2 rotationDelta;
        rotationDelta.x  = mouseDelta.x * MoveLookConstants::RotationGain;   // scale for control sensitivity
        rotationDelta.y  = mouseDelta.y * MoveLookConstants::RotationGain;

        // Update our orientation based on the command.
        m_pitch -= rotationDelta.y;
        m_yaw   += rotationDelta.x;

        // Limit pitch to straight up or straight down.
        float limit = XM_PI / 2.0f - 0.01f;
        m_pitch = __max(-limit, m_pitch);
        m_pitch = __min(+limit, m_pitch);

        // Keep longitude in same range by wrapping.
        if (m_yaw >  XM_PI)
        {
            m_yaw -= XM_PI * 2.0f;
        }
        else if (m_yaw < -XM_PI)
        {
            m_yaw += XM_PI * 2.0f;
        }
        break;
    }
}

タッチのサポートの追加Adding touch support

タッチ コントロールは、タブレット ユーザーのサポートに優れています。Touch controls are great for supporting users with tablets. このゲームでは、特定のゲーム内アクションに合わせて画面の特定領域にゾーンを設定することでタッチ入力を収集します。This game gathers touch input by zoning off certain areas of the screen with each aligning to specific in-game actions. このゲームのタッチ入力では、3 つのゾーンを使用します。This game's touch input uses three zones.

ムーブ/ルックのタッチ画面のレイアウト

次のコマンドは、タッチ コントロールの動作をまとめたものです。The following commands summarize our touch control behavior.

ユーザー入力User input アクションAction
ムーブ四角形Move rectangle タッチ入力は仮想ジョイスティックに変換され、垂直方向のモーションは前/後の位置モーションに変換され、水平方向のモーションは左/右の位置モーションに変換されます。Touch input is converted into a virtual joystick where the vertical motion will be translated into forward/backward position motion and horizontal motion will be translated into left/right position motion.
ファイア四角形Fire rectangle 球体を発射します。Fire a sphere.
ムーブ四角形とファイア四角形の外部をタッチTouch outside of move and fire rectangle カメラ ビューの回転角度 (ピッチとヨー) を変更します。Change the rotation (the pitch and yaw) of the camera view.

MoveLookController は、ポインター ID を確認してイベントがどこで発生したかを判断し、次のいずれかの処理を実行します。The MoveLookController checks the pointer ID to determine where the event occurred, and takes one of the following actions:

  • PointerMoved イベントがムーブまたはファイア四角形で発生した場合は、コントローラーのポインターの位置を更新します。If the PointerMoved event occurred in the move or fire rectangle, update the pointer position for the controller.
  • PointerMoved イベントが画面の残りの部分 (ルック コントロールとして定義されている部分) のどこかで発生した場合は、ルック方向ベクトルのピッチとヨーの変化を計算します。If the PointerMoved event occurred somewhere in the rest of the screen (defined as the look controls), calculate the change in pitch and yaw of the look direction vector.

タッチ コントロールを実装すると、前に Direct2D を使って描画した四角形が、ムーブ、ファイア、ルックの各ゾーンの場所をプレイヤーに示します。Once we've implemented our touch controls, the rectangles we drew earlier using Direct2D will indicate to players where the move, fire, and look zones are.

タッチ コントロール

次に、各コントロールを実装する方法を見てみましょう。Now let's take a look at how we implement each control.

ムーブおよびファイア コントローラーMove and fire controller

画面左下のセクションのムーブ コントローラーの四角形は、方向パッドとして使用されます。The move controller rectangle in the lower left quadrant of the screen is used as a directional pad. この領域内で親指を左右にスライドさせると、プレイヤーが左右に移動し、上下にスライドさせると、カメラが前後に移動します。Sliding your thumb left and right within this space moves the player left and right, while up and down moves the camera forward and backward. これを設定した後、画面の右下のセクションのファイア コントローラーをタップすると、球体が発射されます。After setting this up, tapping the fire controller in the lower right quadrant of the screen fires a sphere.

SetMoveRect メソッドと SetFireRect メソッドは、画面上で各四角形の左上隅と右下隅の位置を指定する 2 つの 2D ベクトルを使用して、入力の四角形を作成します。The SetMoveRect and SetFireRect methods create our input rectangles, taking two, 2D vectors to specify each rectangles' upper left and lower right corner positions on the screen.

次に、m_fireUpperLeftm_fireLowerRight にパラメーターが割り当てられます。これは、ユーザーが四角形の内部をタッチしているかどうかを判断するのに役立ちます。The parameters are then assigned to m_fireUpperLeft and m_fireLowerRight that will help us determine if the user is touching within on of the rectangles.

m_fireUpperLeft  = upperLeft;
m_fireLowerRight = lowerRight;

画面のサイズが変更される場合、これらの四角形は適切なサイズで再描画されます。If the screen is resized, these rectangles are redrawn to the approperiate size.

コントロールのゾーンを設定したら、次はユーザーが実際にコントロールを使用しているかどうかを判断します。Now that we've zoned off our controls, it's time to determine when a user is actually using them. これを行うには、ユーザーがポインターを押したとき、移動したとき、離したときに対応するいくつかのイベント ハンドラーを MoveLookController::InitWindow メソッド内に設定します。To do this, we set up some event handlers in the MoveLookController::InitWindow method for when the user presses, moves, or releases their pointer.

window->PointerPressed +=
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerPressed);

window->PointerMoved +=
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerMoved);

window->PointerReleased +=
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerReleased);

まず、OnPointerPressed メソッド使用して、ユーザーが最初にムーブ四角形またはファイア四角形内を押したときの動作を決定します。We'll first determine what happens when the user first presses within the move or fire rectangles using the OnPointerPressed method. ここで、ユーザーがコントロールにタッチしているかどうか、およびポインターが既にそのコントローラー内にあるかどうかを確認します。Here we check where they're touching a control and if a pointer is already in that controller. これが特定のコントロールにタッチした最初の指である場合は、次の処理を行います。If this is the first finger to touch the specific control, we do the following.

  • タッチの位置を 2D ベクトルとして m _moveFirstDown または m_fireFirstDown に格納します。Store the location of the touchdown in m_moveFirstDown or m_fireFirstDown as a 2D vector.
  • ポインター ID を m _movePointerID または m_firePointerID に割り当てます。Assign the pointer ID to m_movePointerID or m_firePointerID.
  • コントロールのアクティブなポインターが取得できたら、適切な InUse フラグ (m_moveInUse または m_fireInUse) を true に設定します。Set the proper InUse flag (m_moveInUse or m_fireInUse) to true since we now have an active pointer for that control.
    PointerPoint^ point = args->CurrentPoint;
    uint32 pointerID = point->PointerId;
    Point pointerPosition = point->Position;
    PointerPointProperties^ pointProperties = point->Properties;
    auto pointerDevice = point->PointerDevice;
    auto pointerDeviceType = pointerDevice->PointerDeviceType;

    XMFLOAT2 position = XMFLOAT2(pointerPosition.X, pointerPosition.Y);

    case MoveLookControllerState::Active:
        switch (pointerDeviceType)
        {
        case Windows::Devices::Input::PointerDeviceType::Touch:
            // Check to see if this pointer is in the move control.
            if (position.x > m_moveUpperLeft.x &&
                position.x < m_moveLowerRight.x &&
                position.y > m_moveUpperLeft.y &&
                position.y < m_moveLowerRight.y)
            {
                if (!m_moveInUse)         // If no pointer is in this control yet:
                {
                    // Process a DPad touch down event.
                    m_moveFirstDown = position;                 // Save the location of the initial contact
                    m_movePointerID = pointerID;                // Store the pointer using this control
                    m_moveInUse = true;                         // Set InUse flag to signal there is an active move pointer
                }
            }
            // Check to see if this pointer is in the fire control.
            else if (position.x > m_fireUpperLeft.x &&
                position.x < m_fireLowerRight.x &&
                position.y > m_fireUpperLeft.y &&
                position.y < m_fireLowerRight.y)
            {
                if (!m_fireInUse)
                {
                    m_fireLastPoint = position;     // Save the location of the initial contact
                    m_firePointerID = pointerID;    // Store the pointer using this control
                    m_fireInUse = true;             // Set InUse flag to signal there is an active fire pointer
                }
            }
            ...

ユーザーがムーブ コントロールとファイア コントロールのいずれにタッチしているかを特定できたら、プレイヤーが押した指を移動しているかどうかを確認します。Now that we've determined whether the user is touching a move or fire control, we see if the player is making any movements with their pressed finger. MoveLookController::OnPointerMoved メソッドを使用して、どのポインターが移動したかを確認した後、新しい位置を 2D ベクトルとして格納します。Using the MoveLookController::OnPointerMoved method, we check what pointer has moved and then store its new position as a 2D vector.

    PointerPoint^ point = args->CurrentPoint;
    uint32 pointerID = point->PointerId;
    Point pointerPosition = point->Position;
    PointerPointProperties^ pointProperties = point->Properties;
    auto pointerDevice = point->PointerDevice;

    XMFLOAT2 position = XMFLOAT2(pointerPosition.X, pointerPosition.Y);     // convert to allow math

    switch (m_state)
    {
    case MoveLookControllerState::Active:
        // Decide which control this pointer is operating.

        // Move control
        if (pointerID == m_movePointerID)
        {
            m_movePointerPosition = position;       // Save the current position.
        }
        // Look control
        else if (pointerID == m_lookPointerID)
        {
            // ...
        }
        // Fire control
        else if (pointerID == m_firePointerID)
        {
            m_fireLastPoint = position;
        }

ユーザーはコントロール内のジェスチャを行った後、ポインターを離します。Once the user has made their gestures within the controls, they'll release the pointer. MoveLookController::OnPointerReleased メソッドを使用して、離されたポインターを特定し、一連のリセットを実行します。Using the MoveLookController::OnPointerReleased method, we determine which pointer has been released and do a series of resets.

ムーブ コントロールが離された場合は、次の処理を行います。If the move control has been released, we do the following.

  • すべての方向へのプレイヤーの速度を 0 に設定し、ゲーム内でのプレイヤーの移動を防ぎます。Set the velocity of the player to 0 in all directions to prevent them from moving in the game.
  • ユーザーはもうムーブ コントローラーにタッチしていないため、m_moveInUsefalse に切り替えます。Switch m_moveInUse to false since the user is no longer touching the move controller.
  • ムーブ コントローラーにはポインターがなくなったため、ムーブ ポインター ID を 0 に設定します。Set the move pointer ID to 0 since there's no longer a pointer in the move controller.
       if (pointerID == m_movePointerID)
        {
            m_velocity = XMFLOAT3(0, 0, 0);      // Stop on release.
            m_moveInUse = false;
            m_movePointerID = 0;
        }

ファイア コントロールが離された場合、ファイア コントロールにポインターがなくなったため、必要な処理は m_fireInUse フラグを false に切り替えて、ファイア ポインター ID を 0 に設定します。For the fire control, if it has been released all we do is switch the m_fireInUse flag to false and the fire pointer ID to 0 since there's no longer a pointer in the fire control.

        else if (pointerID == m_firePointerID)
        {
            m_fireInUse = false;
            m_firePointerID = 0;
        }

ルック コントローラーLook controller

画面の使用されていない領域用のタッチ デバイスのポインター イベントは、ルック コントローラーとして扱います。We treat touch device pointer events for the unused regions of the screen as the look controller. このゾーンで指をスライドさせると、プレイヤー カメラのピッチとヨー (回転) が変化します。Sliding your finger around this zone changes the pitch and yaw (rotation) of the player camera.

MoveLookController::OnPointerPressed イベントがタッチ デバイスのこの領域で発生し、ゲームの状態が Active に設定されている場合は、ポインター ID が割り当てられます。If the MoveLookController::OnPointerPressed event is raised on a touch device in this region and the game state is set to Active, it's assigned a pointer ID.

    if (!m_lookInUse)   // If no pointer is in this control yet:
    {
        m_lookLastPoint = position;                   // Save the pointer for a later move.
        m_lookPointerID = pointerID;                  // Store the pointer using this control.
        m_lookLastDelta.x = m_lookLastDelta.y = 0;    // These are for smoothing.
        m_lookInUse = true;
    }

MoveLookController::OnPointerPressed メソッドのコード一式については、GitHub で確認できます。You can see the complete code for the MoveLookController::OnPointerPressed method on GitHub.

ここで、MoveLookController は、ルック領域に対応する特定の変数に、イベントを発生させたポインターのポインターの ID を割り当てます。Here the MoveLookController assigns the pointer ID for the pointer that fired the event to a specific variable that corresponds to the look region. ルック領域内でタッチが発生した場合、m_lookPointerID 変数は、そのイベントを発生させたポインターの ID に設定されます。In the case of a touch occuring in the look region, the m_lookPointerID variable is set to the pointer ID that fired the event. ブール変数 m_lookInUse も、コントロールがまだ離されていないことを示すために設定されます。A boolean variable, m_lookInUse, is also set to indicate that the control has not yet been released.

次は、このゲーム サンプルで PointerMoved タッチ スクリーン イベントを処理する方法を見てみましょう。Now, let's look at how the game sample handles the PointerMoved touch screen event.

MoveLookController::OnPointerMoved メソッド内で、どのような種類のポインター ID がイベントに割り当てられているかを確認します。Within the MoveLookController::OnPointerMoved method, we check to see what kind of pointer ID has been assigned to the event. m_lookPointerID の場合は、ポインターの位置の変更を計算します。If it's m_lookPointerID, we calculate the change in position of the pointer. このデルタを使用して、どの程度の回転を変更する必要があるかを計算します。We then use this delta to calculate how much the rotation should change. 最後に、プレイヤーの回転を変更するためにゲームで使用される m _pitchm _yaw を更新します。Finally we're at a point where we can update the m_pitch and m_yaw to be used in the game to change the player rotation.

    else if (pointerID == m_lookPointerID)     // This is the look pointer.
    {
        // Look control
        XMFLOAT2 pointerDelta;
        pointerDelta.x = position.x - m_lookLastPoint.x;        // How far did the pointer move.
        pointerDelta.y = position.y - m_lookLastPoint.y;

        XMFLOAT2 rotationDelta;
        rotationDelta.x = pointerDelta.x * MoveLookConstants::RotationGain;       // Scale for control sensitivity.
        rotationDelta.y = pointerDelta.y * MoveLookConstants::RotationGain;
        m_lookLastPoint = position;                             // Save for the next time through.

        // Update our orientation based on the command.
        m_pitch -= rotationDelta.y;
        m_yaw   += rotationDelta.x;

        // Limit pitch to straight up or straight down.
        float limit = XM_PI / 2.0f - 0.01f;
        m_pitch = __max(-limit, m_pitch);
        m_pitch = __min(+limit, m_pitch);M_PI / 2.0f, m_pitch);
        }

確認する最後の要素は、このゲーム サンプルで PointerReleased タッチ スクリーン イベントがどのように処理されているかです。The last piece we'll look at is how the game sample handles the PointerReleased touch screen event. ユーザーがタッチ ジェスチャを終了し、画面から指を離すと、MoveLookController::OnPointerReleased が開始されます。Once the user has finished the touch gesture and removed their finger from the screen, MoveLookController::OnPointerReleased is initiated. PointerReleased イベントを発生させたポインターの ID が前に記録されたムーブ ポインターの ID である場合は、プレイヤーがルック領域から指を離したため、MoveLookController は速度を 0 に設定します。If the ID of the pointer that fired the PointerReleased event is the ID of the previously recorded move pointer, the MoveLookController sets the velocity to 0 because the player has stopped touching the look area.

    else if (pointerID == m_lookPointerID)
    {
        m_lookInUse = false;
        m_lookPointerID = 0;
    }

マウスとキーボードのサポートの追加Adding mouse and keyboard support

このゲームには、キーボードとマウス用に次のコントロール レイアウトが含まれています。This game has the following control layout for keyboard and mouse.

ユーザー入力User input アクションAction
WW プレイヤーを前へ移動します。Move player forward
AA プレイヤーを左へ移動します。Move player left
SS プレイヤーを後ろへ移動します。Move player backward
DD プレイヤーを右へ移動します。Move player right
XX ビューを上へ移動します。Move view up
Space キーSpace bar ビューを下へ移動します。Move view down
PP ゲームを一時停止します。Pause the game
マウスの移動Mouse movement カメラ ビューの回転角度 (ピッチとヨー) を変更します。Change the rotation (the pitch and yaw) of the camera view
マウスの左ボタンLeft mouse button 球体を発射します。Fire a sphere

キーボードを使用するために、ゲーム サンプルは、MoveLookController::InitWindow メソッド内で 2 つの新しいイベント CoreWindow::KeyUpCoreWindow::KeyDown を登録します。To use the keyboard, the game sample registers two new events, CoreWindow::KeyUp and CoreWindow::KeyDown, within the MoveLookController::InitWindow method. これらのイベントは、キーを押す操作と離す操作を処理します。These events handle the press and release of a key.

window->KeyDown +=
        ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &MoveLookController::OnKeyDown);

window->KeyUp +=
        ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &MoveLookController::OnKeyUp);

マウスは、ポインターを使いますが、タッチ コントロールとは扱いが少し異なります。The mouse is treated a little differently from the touch controls even though it uses a pointer. このコントロール レイアウトに合わせて、MoveLookController は、マウスが移動されるたびにカメラを回転させ、マウスの左ボタンが押されたときに発射します。To align with our control layout, the MoveLookController rotates the camera whenever the mouse is moved, and fires when the left mouse button is pressed.

これは、MoveLookControllerOnPointerPressed メソッドで処理されます。This is handled in the OnPointerPressed method of the MoveLookController.

このメソッドで、Windows::Devices::Input::PointerDeviceType 列挙型を使って、使用されているポインター デバイスの種類を確認します。In this method we check to see what type of pointer device is being used with the Windows::Devices::Input::PointerDeviceType enum. ゲームが Active であり、PointerDeviceTypeTouch ではない場合は、マウス入力と見なします。If the game is Active and the PointerDeviceType isn't Touch, we assume it's mouse input.

    case MoveLookControllerState::Active:
        switch (pointerDeviceType)
        {
        case Windows::Devices::Input::PointerDeviceType::Touch:
            // Behavior for touch controls

        default:
        // Behavior for mouse controls
            bool rightButton = pointProperties->IsRightButtonPressed;
            bool leftButton = pointProperties->IsLeftButtonPressed;

            if (!m_autoFire && (!m_mouseLeftInUse && leftButton))
            {
                m_firePressed = true;
            }

            if (!m_mouseInUse)
            {
                m_mouseInUse = true;
                m_mouseLastPoint = position;
                m_mousePointerID = pointerID;
                m_mouseLeftInUse = leftButton;
                m_mouseRightInUse = rightButton;
                m_lookLastDelta.x = m_lookLastDelta.y = 0;          // These are for smoothing.
            }
            break;
        }

プレイヤーがいずれかのマウス ボタンを押すことをやめると、CoreWindow::PointerReleased マウス イベントが発生し、MoveLookController::OnPointerReleased メソッドが呼び出されて、入力が完了します。When the player stops pressing one of the mouse buttons, the CoreWindow::PointerReleased mouse event is raised, calling the MoveLookController::OnPointerReleased method, and the input is complete. ここで、押していたマウスの左ボタンから指を離した場合、球体の発射は停止します。At this point, spheres will stop firing if the left mouse button was being pressed and is now released. ルックは常に有効であるため、ゲームでは同じマウス ポインターを使い続けて、進行中のルック イベントを追跡します。Because look is always enabled, the game continues to use the same mouse pointer to track the ongoing look events.

    case MoveLookControllerState::Active:
        // Touch points
        if (pointerID == m_movePointerID)
        {
            // Stop movement
        }
        else if (pointerID == m_lookPointerID)
        {
            // Stop look rotation
        }
        // Fire button has been released
        else if (pointerID == m_firePointerID)
        {
            // Stop firing
        }
        // Mouse point
        else if (pointerID == m_mousePointerID)
        {
            bool rightButton = pointProperties->IsRightButtonPressed;
            bool leftButton = pointProperties->IsLeftButtonPressed;

            // Mouse no longer in use so stop firing
            m_mouseInUse = false;

            // Don't clear the mouse pointer ID so that Move events still result in Look changes.
            // m_mousePointerID = 0;
            m_mouseLeftInUse = leftButton;
            m_mouseRightInUse = rightButton;
        }
        break;
    }
}

次に、サポートしている最後のコントロールの種類である、ゲームパッドを見てみましょう。Now let's look at the last control type we'll be supporting: gamepads. ゲームパッドは、ポインター オブジェクトを使わないため、タッチ コントロールやマウス コントロールとは別に処理されます。Gamepads are handled separately from the touch and mouse controls since they doesn't use the pointer object. このため、いくつかの新しいイベント ハンドラーとメソッドを追加する必要があります。Because of this, a few new event handlers and methods will need to be added.

ゲームパッドのサポートの追加Adding gamepad support

このゲームでは、ゲームパッドのサポートは Windows.Gaming.Input API の呼び出しによって追加されます。For this game, gamepad support is added by calls to the Windows.Gaming.Input APIs. この一連の API は、レーシング ハンドルやフライト スティックなどの、ゲーム コントローラー入力へのアクセスを提供します。This set of APIs provides access to game controller inputs like racing wheels and flight sticks.

このゲームのゲームパッド コントロールは次のようになります。The following will be our gamepad controls.

ユーザー入力User input アクションAction
左アナログ スティックLeft analog stick プレイヤーを移動します。Move player
右アナログ スティックRight analog stick カメラ ビューの回転角度 (ピッチとヨー) を変更します。Change the rotation (the pitch and yaw) of the camera view
右トリガーRight trigger 球体を発射します。Fire a sphere
スタート/メニュー ボタンStart/Menu button ゲームを一時停止または再開します。Pause or resume the game

InitWindow メソッドに、ゲームパッドが追加されたか、削除されたかを判断するために 2 つの新しいイベントを追加します。In the InitWindow method, we add two new events to determine if a gamepad has been added or removed. これらのイベントは m_gamepadsChanged プロパティを更新します。These events update the m_gamepadsChanged property. これは、既知のゲームパッドの一覧が変更されたかどうかを確認するために、UpdatePollingDevices メソッドで使用されます。This is used in the UpdatePollingDevices method to check if the the list of known gamepads has changed.

    // Detect gamepad connection and disconnection events.
    Gamepad::GamepadAdded +=
        ref new EventHandler<Gamepad^>(this, &MoveLookController::OnGamepadAdded);

    Gamepad::GamepadRemoved +=
        ref new EventHandler<Gamepad^>(this, &MoveLookController::OnGamepadRemoved);

UpdatePollingDevices メソッドThe UpdatePollingDevices method

MoveLookController インスタンスの UpdatePollingDevices メソッドは、すぐにゲームパッドが接続されているかどうかを確認します。The UpdatePollingDevices method of the MoveLookController instance immediately checks to see if a gamepad is connected. ゲームパッドが接続されている場合は、Gamepad.GetCurrentReading を使用して、その状態の読み取りを開始します。If one is, we'll start reading its state with Gamepad.GetCurrentReading. これによって GamepadReading 構造体が返され、クリックされていたボタンや移動したサムスティックを確認できます。This returns the GamepadReading struct, allowing us to check what buttons have been clicked or thumbsticks moved.

ゲームの状態が WaitForInput である場合、ゲームを再開できるように、コントローラーのスタート/メニュー ボタンのみをリッスンします。If the state of the game is WaitForInput, we only listen for the Start/Menu button of the controller so that the game can be resumed.

状態が Active である場合は、ユーザーの入力を確認し、どのようなゲーム内アクションが発生する必要があるかを特定します。If it's Active, we check the user's input and determine what in-game action needs to happen. たとえば、ユーザーが特定の方向に左アナログ スティックを動かした場合、これによって、ゲームはスティックを動かした方向にプレイヤーを移動する必要があることを認知します。For instance, if the user moved the left analog stick in a specific direction, this lets the game know we need to move the player in the direction the stick is being moved. 特定の方向へのスティックの動きは、デッド ゾーンの半径より大きく登録される必要があります。大きく登録されないと、何も起きません。The movement of the stick in a specific direction must register as larger than the radius of the dead zone; otherwise, nothing will happen. このデッド ゾーンの半径は、"ドリフト" を防止するために必要です。この機能では、プレイヤーの指がスティック上にあるときに、指のわずかな動きをコントローラーが感知します。This dead zone radius is necessary to prevent "drifting," which is when the controller picks up small movements from the player's thumb as it rests on the stick. デッド ゾーンがない場合、ユーザーはコントロールの感度が高すぎると感じることがあります。Without dead zones, the controls can appear too sensitive to the user.

サム スティックの入力は、x 軸と y 軸の両方について -1 ~ 1 の範囲です。Thumbstick input is between -1 and 1 for both the x and y axis. 次の定数は、サムスティックのデッドゾーンの半径を指定します。The following consant specifies the radius of the thumbstick dead zone.

#define THUMBSTICK_DEADZONE 0.25f

この変数を使用して、アクション可能なサムスティックの入力の処理を開始します。Using this variable, we'll then begin processing actionable thumbstick input. いずれかの軸で値が [-1, -.26] または [.26, 1] から移動が発生します。Movement would occur with a value from [-1, -.26] or [.26, 1] on either axis.

サムスティックのデッド ゾーン

UpdatePollingDevices メソッドの次の部分は、左右のサム スティックを処理します。This piece of the UpdatePollingDevices method handles the left and right thumbsticks. 各スティックの X と Y の値について、デッド ゾーンの外側であるかどうかがチェックされます。Each stick's X and Y values are checked to see if they are outside of the dead zone. いずれかまたは両方がこれに該当する場合、対応するコンポーネントを更新します。If one or both are, we'll update the corresponding component. たとえば、左サムスティックが X 軸に沿って左に移動された場合、m_moveCommandベクトルの x コンポーネントに -1 を加算します。For example, if the left thumbstick is being moved left along the X axis, we'll add -1 to the x component of the m_moveCommand vector. このベクトルは、すべてのデバイスのすべての動きを集計するために使用されるもので、後でプレイヤーが移動する場所を計算するために使用されます。This vector is what will be used to aggregate all movements across all devices and will later be used to calculate where the player should move.

        // Use the left thumbstick to control the eye point position (position of the player)

        // Check if left thumbstick is outside of dead zone on x axis
        if (reading.LeftThumbstickX > THUMBSTICK_DEADZONE ||
            reading.LeftThumbstickX < -THUMBSTICK_DEADZONE)
        {
            // Get value of left thumbstick's position on x axis
            float x = static_cast<float>(reading.LeftThumbstickX);
            // Set the x of the move vector to 1 if the stick is being moved right.
            // Set to -1 if moved left. 
            m_moveCommand.x -= (x > 0) ? 1 : -1;
        }

        // Check if left thumbstick is outside of dead zone on y axis
        if (reading.LeftThumbstickY > THUMBSTICK_DEADZONE ||
            reading.LeftThumbstickY < -THUMBSTICK_DEADZONE)
        {
            // Get value of left thumbstick's position on y axis
            float y = static_cast<float>(reading.LeftThumbstickY);
            // Set the y of the move vector to 1 if the stick is being moved forward.
            // Set to -1 if moved backwards. 
            m_moveCommand.y += (y > 0) ? 1 : -1;
        }

左スティックが動きを制御するのと同様に、右スティックはカメラの回転を制御します。Similar to how the left stick controls movement, the right stick controls the rotation of the camera.

右サムスティックの動作は、マウスとキーボード コントロールの設定でのマウス移動の動作と一致します。The right thumb stick behavior aligns with the behavior of mouse movement in our mouse and keyboard control setup. スティックがデッド ゾーン外にある場合は、ポインターの現在の位置と、ユーザーが現在見ようとしている位置の差を計算します。If the stick is outside of the dead zone, we calculate the difference between the current pointer position and where the user is now trying to look. このポインターの位置の変化 (pointerDelta) を使用して、後で Update メソッドで適用されるカメラの回転のピッチとヨーが更新されます。This change in pointer position (pointerDelta) is then used to update the pitch and yaw of the camera rotation that later get applied in our Update method. pointerDelta ベクトルには見覚えがあるかもしれません。これは、マウスとタッチ入力でポインターの位置の変化を追跡するために、MoveLookController::OnPointerMoved メソッドでも使用されています。The pointerDelta vector may look familiar because it's also used in the MoveLookController::OnPointerMoved method to keep track of change in pointer position for our mouse and touch inputs.

        // Use the right thumbstick to control the look at position

        XMFLOAT2 pointerDelta;

        // Check if right thumbstick is outside of deadzone on x axis
        if (reading.RightThumbstickX > THUMBSTICK_DEADZONE ||
            reading.RightThumbstickX < -THUMBSTICK_DEADZONE)
        {
            float x = static_cast<float>(reading.RightThumbstickX);
            // Register the change in the pointer along the x axis
            pointerDelta.x = x * x * x; 
        }
        // No actionable thumbstick movement. Register no change in pointer.
        else
        {
            pointerDelta.x = 0.0f;
        }
        // Check if right thumbstick is outside of deadzone on y axis
        if (reading.RightThumbstickY > THUMBSTICK_DEADZONE ||
            reading.RightThumbstickY < -THUMBSTICK_DEADZONE)
        {
            float y = static_cast<float>(reading.RightThumbstickY);
            // Register the change in the pointer along the y axis
            pointerDelta.y = y * y * y;
        }
        else
        {
            pointerDelta.y = 0.0f;
        }

        XMFLOAT2 rotationDelta;
        rotationDelta.x = pointerDelta.x *  0.08f;       // Scale for control sensitivity.
        rotationDelta.y = pointerDelta.y *  0.08f;

        // Update our orientation based on the command.
        m_pitch += rotationDelta.y;
        m_yaw += rotationDelta.x;

        // Limit pitch to straight up or straight down.
        m_pitch = __max(-XM_PI / 2.0f, m_pitch);
        m_pitch = __min(+XM_PI / 2.0f, m_pitch);

このゲームのコントロールは、球体を発射する機能がなければ完成しません。The game's controls wouldn't be complete without the ability to fire spheres!

この UpdatePollingDevices メソッドは、右のトリガーが押されているかどうかも確認します。This UpdatePollingDevices method also checks if the right trigger is being pressed. 右のトリガーが押された場合、m_firePressed プロパティは true になり、球体の発射を開始する必要があることをゲームに通知します。If it is, our m_firePressed property is flipped to true, signaling to the game that spheres should start firing.

    if (reading.RightTrigger > TRIGGER_DEADZONE)
    {
        if (!m_autoFire && !m_gamepadTriggerInUse)
        {
            m_firePressed = true;
        }

        m_gamepadTriggerInUse = true;
    }
    else
    {
        m_gamepadTriggerInUse = false;
    }

Update メソッドThe Update method

締めくくりとして、Update メソッドについて深く掘り下げてみましょう。To wrap things up, let's dig deeper into the Update method. このメソッドは、プレイヤーがサポートされている入力を使用して行ったすべての動きや回転をマージして、ゲーム ループがアクセスする速度ベクトルを生成し、ピッチとヨーの値を更新します。This method merges any movements or rotations that the player made with any supported input to generate a velocity vector and update our pitch and yaw values for our game loop to access.

Update メソッドは、UpdatePollingDevices を呼び出してコントローラーの状態を更新することから処理を開始します。The Update method kicks things off by calling UpdatePollingDevices to update the state of the controller. また、このメソッドは、ゲームパッドからのすべての入力を収集し、その動きを m_moveCommand ベクトルに追加します。This method also gathers any input from a gamepad and adds its movements to the m_moveCommand vector.

このサンプルの Update メソッドでは、以下の入力の確認を実行します。In our Update method we then perform the following input checks.

  • プレイヤーがムーブ コントローラーの四角形を使用している場合は、ポインターの位置の変化を確認し、それを使用してユーザーがコントローラーのデッド ゾーンの外側にポインターを移動したかどうかを計算します。If the player is using the move controller rectangle, we'll then determine the change in pointer position and use that to calculate if the user has moved the pointer out of the controller's dead zone. デッド ゾーンの外側である場合、仮想ジョイスティックの値を使用して m_moveCommand ベクトル プロパティが更新されます。If outside of the dead zone, the m_moveCommand vector property is then updated with the virtual joystick value.
  • 移動のキーボード入力のいずれかが押されている場合、m_moveCommand ベクトルの対応するコンポーネントに 1.0f または -1.0f の値が追加されます。1.0f は前進、-1.0f は後退です。If any of the movement keyboard inputs are pressed, a value of 1.0f or -1.0f are added in the corresponding component of the m_moveCommand vector — 1.0f for forward, and -1.0f for backward.

すべての移動入力を考慮した後、m_moveCommand ベクトルに対していくつかの計算を実行し、ゲーム ワールドでのプレイヤーの方向を表す新しいベクトルを生成します。Once all movement input has been taken into account, we then run the m_moveCommand vector through some calculations to generate a new vector that represents the direction of the player with regards to the game world. 次に、ワールドでの動きを取得し、それをその方向への速度としてプレイヤーに適用します。We then take our movements in relation to the world and apply them to the player as velocity in that direction. 最後に、m_moveCommand ベクトルを (0.0f, 0.0f, 0.0f) にリセットして、次のゲーム フレームを処理できるようにすべてを準備します。Finally we reset the m_moveCommand vector to (0.0f, 0.0f, 0.0f) so that everything is ready for the next game frame.

void MoveLookController::Update()
{
    // Get any gamepad input and update state
    UpdatePollingDevices();

    // Get change in 
    if (m_moveInUse)
    {
        XMFLOAT2 pointerDelta;

        pointerDelta.x = m_movePointerPosition.x - m_moveFirstDown.x;
        pointerDelta.y = m_movePointerPosition.y - m_moveFirstDown.y;

        // Figure out the command from the virtual joystick.
        XMFLOAT3 commandDirection = XMFLOAT3(0.0f, 0.0f, 0.0f);
        if (fabsf(pointerDelta.x) > 16.0f)         // Leave 32 pixel-wide dead spot for being still.
            m_moveCommand.x -= pointerDelta.x/fabsf(pointerDelta.x);

        if (fabsf(pointerDelta.y) > 16.0f)
            m_moveCommand.y -= pointerDelta.y/fabsf(pointerDelta.y);
    }

    // Poll our state bits set by the keyboard input events.
    if (m_forward)
    {
        m_moveCommand.y += 1.0f;
    }
    if (m_back)
    {
        m_moveCommand.y -= 1.0f;
    }
    if (m_left)
    {
        m_moveCommand.x += 1.0f;
    }
    if (m_right)
    {
        m_moveCommand.x -= 1.0f;
    }
    if (m_up)
    {
        m_moveCommand.z += 1.0f;
    }
    if (m_down)
    {
        m_moveCommand.z -= 1.0f;
    }

    // Make sure that 45deg cases are not faster.
    if (fabsf(m_moveCommand.x) > 0.1f ||
        fabsf(m_moveCommand.y) > 0.1f ||
        fabsf(m_moveCommand.z) > 0.1f)
    {
        XMStoreFloat3(&m_moveCommand, XMVector3Normalize(XMLoadFloat3(&m_moveCommand)));
    }

    // Rotate command to align with our direction (world coordinates).
    XMFLOAT3 wCommand;
    wCommand.x =  m_moveCommand.x * cosf(m_yaw) - m_moveCommand.y * sinf(m_yaw);
    wCommand.y =  m_moveCommand.x * sinf(m_yaw) + m_moveCommand.y * cosf(m_yaw);
    wCommand.z =  m_moveCommand.z;

    // Scale for sensitivity adjustment.
    // Our velocity is based on the command. Y is up.
    m_velocity.x = -wCommand.x * MoveLookConstants::MovementGain;
    m_velocity.z =  wCommand.y * MoveLookConstants::MovementGain;
    m_velocity.y =  wCommand.z * MoveLookConstants::MovementGain;

    // Clear movement input accumulator for use during next frame.
    m_moveCommand = XMFLOAT3(0.0f, 0.0f, 0.0f);
}

次の手順Next steps

これで、コントロールが追加されましたが、臨場感のあるゲームを作成するためにもう 1 つ追加しなければならない機能として、サウンドがあります。Now that we have added our controls, there's another feature we need to add to create an immersive game: sound! ミュージックとサウンド効果はどのゲームでも重要であるため、次の「サウンドの追加」で詳しく説明します。Music and sound effects are important to any game, so let's discuss adding sound next.