Share via


遊戲的觸控控制項

瞭解如何使用 DirectX 將基本觸控控制項新增至通用 Windows 平台 (UWP) C++ 遊戲。 本文將示範如何新增觸控式控制項,以在 Direct3D 環境中移動固定平面相機,其中使用手指或手寫筆拖曳可移動相機視角。

您可將這些控制項整合至遊戲,讓玩家拖曳以捲動或移動瀏覽 3D 環境,例如地圖或遊戲場。 例如,在策略或拼圖遊戲中使用這些控制項,可讓玩家透過左右移動瀏覽以檢視大於螢幕的遊戲環境。

注意程式碼也適用於滑鼠移動瀏覽控制項。 指標相關事件是由 Windows 執行階段 API 抽象化,因此可處理觸控或滑鼠指標事件。

 

目標

  • 在 DirectX 遊戲中建立簡單的觸控拖曳控制項,以使用固定平面相機移動瀏覽。

設定基本觸控事件基礎結構

首先,我們會定義基本控制器類型,在此案例中是指 CameraPanController。 這裡會將控制器定義為抽象概念,也就是使用者可執行的一組行為。

CameraPanController 類別是相機控制器狀態的相關資訊集合,會定期重新整理。此方式可讓應用程式從該類別的更新迴圈取得該資訊。

using namespace Windows::UI::Core;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::Devices::Input;
#include <directxmath.h>

// Methods to get input from the UI pointers
ref class CameraPanController
{
}

現在,我們要建立標頭以定義相機控制器的狀態,以及實作相機控制器互動的基本方法和事件處理常式。

ref class CameraPanController
{
private:
    // Properties of the controller object
    DirectX::XMFLOAT3 m_position;               // the position of the camera

    // Properties of the camera pan control
    bool m_panInUse;                
    uint32 m_panPointerID;          
    DirectX::XMFLOAT2 m_panFirstDown;           
    DirectX::XMFLOAT2 m_panPointerPosition;   
    DirectX::XMFLOAT3 m_panCommand;         
    
internal:
    // Accessor to set the position of the controller
    void SetPosition( _In_ DirectX::XMFLOAT3 pos );

       // Accessor to set the fixed "look point" of the controller
       DirectX::XMFLOAT3 get_FixedLookPoint();

    // Returns the position of the controller object
    DirectX::XMFLOAT3 get_Position();

public:

    // Methods to get input from the UI pointers
    void OnPointerPressed(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    void OnPointerMoved(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    void OnPointerReleased(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::PointerEventArgs^ args
        );

    // Set up the Controls supported by this controller
    void Initialize( _In_ Windows::UI::Core::CoreWindow^ window );

    void Update( Windows::UI::Core::CoreWindow ^window );

};  // Class CameraPanController

私用欄位包含相機控制器的目前狀態。 請見以下說明。

  • m_position 是相機在場景空間中的位置。 在此範例中,z 座標值固定在 0。 我們可使用 DirectX::XMFLOAT2 表示此值,但為了此範例說明和日後擴充,因此使用 DirectX::XMFLOAT3。 透過 get_Position 屬性將此值傳遞給應用程式,使其根據值更新檢視區。
  • m_panInUse 是布爾值,指出移動瀏覽操作是否進行中;或者更具體地說,玩家是否正在觸控螢幕並移動相機。
  • m_panPointerID 是指標的唯一識別碼。 我們不會在範例中使用此功能,但建議將控制器狀態類別與特定指標產生關聯。
  • m_panFirstDown 是指玩家在畫面上初次觸控螢幕,或在相機移動瀏覽動作期間點按滑鼠的位置點。 稍後會使用此值設定死區,以防止觸控螢幕或稍微晃動滑鼠時出現抖動。
  • m_panPointerPosition 是玩家目前在畫面上移動指標的位置點。 檢視該欄位相對於 m_panFirstDown 的值,可判斷玩家想朝哪個方向移動。
  • m_panCommand 是相機控制器的最終計算命令:上、下、左或右。 由於我們正使用固定於 x-y 平面的相機,因此這可能是 DirectX::XMFLOAT2 值。

我們可使用這 3 個事件處理常式更新相機控制器的狀態資訊。

  • OnPointerPressed:當玩家以手指點按觸控介面,且指標移至按下的座標,應用程式即呼叫此事件處理程式。
  • OnPointerMoved:當玩家在觸控介面撥動手指,應用程式即呼叫此事件處理常式。 其會以拖曳路徑的新座標進行更新。
  • OnPointerReleased:當玩家將手指從觸控介面上按住的位置移開,應用程式即呼叫此事件處理程式。

最後,我們使用這些方法和屬性初始化、存取及更新相機控制器的狀態資訊。

  • Initialize:應用程式會呼叫此事件處理常式,以初始化控制項並將其附加至描述顯示視窗的 CoreWindow 物件。
  • SetPosition:應用程式會呼叫此方法,以設定控制項在場景空間中的 (x、y、和 z) 座標。 請注意,本教學課程中的 z 座標都為 0。
  • get_Position:應用程式會存取此屬性,以取得相機在場景空間中的目前位置。 您可使用此屬性向應用程式傳遞目前的相機位置。
  • get_FixedLookPoint:應用程式會存取此屬性,以取得控制器相機目前朝向的位置點。 在此範例中,其鎖定在 x-y 平面的法線。
  • Update 是用來讀取控制器狀態並更新相機位置的方法。 您持續從應用程式的主要迴圈呼叫此 <something>,以重新整理相機控制器資料和相機在場景空間中的位置。

現在,您已具備實作觸控控制項所需的所有元件。 您可偵測觸控或滑鼠指標事件發生的時間和位置,以及動作是什麼。 您可設定相機相對於場景空間的位置和方向,並追蹤變更。 最後,您可將新的相機位置傳遞給呼叫的應用程式。

接著,我們來連結這些元件。

建立基本觸控事件

Windows 執行階段事件發送器提供以下 3 個事件,我們希望由應用程式處理:

這些事件會在 CoreWindow 類型實作。 我們假設您有要使用的 CoreWindow 物件。 如需詳細資訊,請參閱<如何設定 UWP C++ 應用程式以顯示 DirectX 檢視>。

如果應用程式執行時引發這些事件,則處理常式會更新私用欄位中定義的相機控制器狀態資訊。

首先,填入觸控指標事件處理常式。 在第一個事件處理常式 OnPointerPressed 中,我們從 CoreWindow 取得指標的 x-y 座標,使用者觸控螢幕或點按滑鼠時,該座標可用來管理顯示結果。

OnPointerPressed

void CameraPanController::OnPointerPressed(
                                           _In_ CoreWindow^ sender,
                                           _In_ PointerEventArgs^ args)
{
    // Get the current pointer position.
    uint32 pointerID = args->CurrentPoint->PointerId;
    DirectX::XMFLOAT2 position = DirectX::XMFLOAT2( args->CurrentPoint->Position.X, args->CurrentPoint->Position.Y );

    auto device = args->CurrentPoint->PointerDevice;
    auto deviceType = device->PointerDeviceType;
    

       if ( !m_panInUse )   // If no pointer is in this control yet.
    {
       m_panFirstDown = position;                   // Save the location of the initial contact.
       m_panPointerPosition = position;
       m_panPointerID = pointerID;              // Store the id of the pointer using this control.
       m_panInUse = TRUE;
    }
    
}

我們使用此事件處理常式,將 m_panInUse 設為 TRUE,指示目前的 CameraPanController 執行個體應將相機控制器視為使用中。 這樣當應用程式呼叫 Update 時,即可使用目前的位置資料更新檢視區。

對於使用者觸控螢幕或在顯示視窗中點按滑鼠時的相機動作,我們已建立好基底值,接下來我們必須決定,當使用者觸控螢幕並拖曳或按下滑鼠並移動時,該怎麼做。

每當指標移動、每次玩家在螢幕上拖曳,都會引發 OnPointerMoved 事件處理常式。 我們就是透過這個方式讓應用程式知道指標的目前位置。

OnPointerMoved

void CameraPanController::OnPointerMoved(
                                        _In_ CoreWindow ^sender,
                                        _In_ PointerEventArgs ^args)
{
    uint32 pointerID = args->CurrentPoint->PointerId;
    DirectX::XMFLOAT2 position = DirectX::XMFLOAT2( args->CurrentPoint->Position.X, args->CurrentPoint->Position.Y );

    m_panPointerPosition = position;
}

最後,我們需要在玩家停止觸控螢幕時,停用相機的移動瀏覽行為。 我們使用 OnPointerReleased (於 PointerReleased 觸發時呼叫),將 m_panInUse 設為 FALSE,然後關閉相機移動瀏覽動作,並將指標識別碼設為 0。

OnPointerReleased

void CameraPanController::OnPointerReleased(
                                             _In_ CoreWindow ^sender,
                                             _In_ PointerEventArgs ^args)
{
    uint32 pointerID = args->CurrentPoint->PointerId;
    DirectX::XMFLOAT2 position = DirectX::XMFLOAT2( args->CurrentPoint->Position.X, args->CurrentPoint->Position.Y );

    m_panInUse = FALSE;
    m_panPointerID = 0;
}

初始化觸控控制項及控制器狀態

接著,我們連結事件,並初始化相機控制器的所有基本狀態欄位。

Initialize

void CameraPanController::Initialize( _In_ CoreWindow^ window )
{

    // Start receiving touch/mouse events.
    window->PointerPressed += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerPressed);

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

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


    // Initialize the state of the controller.
    m_panInUse = FALSE;             
    m_panPointerID = 0;

    //  Initialize this as it is reset on every frame.
    m_panCommand = DirectX::XMFLOAT3( 0.0f, 0.0f, 0.0f );

}

Initialize 會將應用程式的 CoreWindow 執行個體作為參數進行參照,然後將我們開發的事件處理常式註冊到 CoreWindow 上的適當事件。

取得和設定相機控制器的位置

現在,我們會定義一些方法,以取得和設定場景空間中相機控制器的位置。

void CameraPanController::SetPosition( _In_ DirectX::XMFLOAT3 pos )
{
    m_position = pos;
}

// Returns the position of the controller object
DirectX::XMFLOAT3 CameraPanController::get_Position()
{
    return m_position;
}

DirectX::XMFLOAT3 CameraPanController::get_FixedLookPoint()
{
    // For this sample, we don't need to use the trig functions because our
    // look point is fixed. 
    DirectX::XMFLOAT3 result= m_position;
    result.z += 1.0f;
    return result;    

}

SetPosition:如果需要將相機控制器位置設為特定點,可從應用程式呼叫此公用方法。

get_Position 是最重要的公用屬性:應用程式可透過此屬性,取得相機控制器在場景空間中的目前位置,然後據此更新檢視區。

get_FixedLookPoint:在此範例中,此公用屬性可取得 x-y 平面法線的視點。 若想為固定相機建立更多斜角,可在計算 x、y 和 z 座標值時,將此方法變更為使用三角函數 sin 和 cos。

更新相機控制器的狀態資訊

現在我們來進行計算,將 m_panPointerPosition 追蹤的指標座標資訊,轉換成 3D 場景空間中各自對應的新座標資訊。 每次主要應用程式迴圈重新整理時,應用程式都會呼叫此方法。 其中,我們會計算想要傳遞至應用程式的新位置資訊,以用來更新檢視矩陣,再投影到檢視區。


void CameraPanController::Update( CoreWindow ^window )
{
    if ( m_panInUse )
    {
        pointerDelta.x = m_panPointerPosition.x - m_panFirstDown.x;
        pointerDelta.y = m_panPointerPosition.y - m_panFirstDown.y;

        if ( pointerDelta.x > 16.0f )        // Leave 32 pixel-wide dead spot for being still.
            m_panCommand.x += 1.0f;
        else
            if ( pointerDelta.x < -16.0f )
                m_panCommand.x += -1.0f;

        if ( pointerDelta.y > 16.0f )        
            m_panCommand.y += 1.0f;
        else
            if (pointerDelta.y < -16.0f )
                m_panCommand.y += -1.0f;
    }

       DirectX::XMFLOAT3 command = m_panCommand;
   
    // Our velocity is based on the command.
    DirectX::XMFLOAT3 Velocity;
    Velocity.x =  command.x;
    Velocity.y =  command.y;
    Velocity.z =  0.0f;

    // Integrate
    m_position.x = m_position.x + Velocity.x;
    m_position.y = m_position.y + Velocity.y;
    m_position.z = m_position.z + Velocity.z;

    // Clear the movement input accumulator for use during the next frame.
    m_panCommand = DirectX::XMFLOAT3( 0.0f, 0.0f, 0.0f );

}

我們不希望因為觸控或滑鼠抖動,導致相機移動瀏覽晃動,因此在指標周圍設定一個直徑為 32 像素的死區。 此外,我們也有速度值,在此案例中,該速度值與指標經過死區後的像素周遊成 1:1 比例。 您可以調整此行為,以減緩或加快移動速度。

使用新的相機位置更新檢視矩陣

我們現在可取得相機聚焦的場景空間座標,且每當您指示應用程式執行此動作時 (例如,在主要應用程式迴圈中每 60 秒一次),該座標都會更新。 此虛擬程式碼提供可實作的呼叫行為建議:

 myCameraPanController->Update( m_window ); 

 // Update the view matrix based on the camera position.
 myCamera->MyMethodToComputeViewMatrix(
        myController->get_Position(),        // The position in the 3D scene space.
        myController->get_FixedLookPoint(),      // The point in the space we are looking at.
        DirectX::XMFLOAT3( 0, 1, 0 )                    // The axis that is "up" in our space.
        );  

恭喜! 您已在遊戲中實作一組簡單的相機移動瀏覽觸控控制項。