共用方式為


遊戲的觸控控件

瞭解如何使用 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 是讀取控制器狀態並更新相機位置的方法。 您從應用程式的主迴圈持續呼叫此 <>,以重新整理相機控制器數據和場景空間中的相機位置。

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

現在,讓我們將這些部分連接在一起。

建立基本觸控事件

Windows 執行時間事件發送器提供 3 個我們想要應用程式處理的事件:

這些事件會在 CoreWindow 類型上實作。 我們假設您有一個 CoreWindow 物件可使用。 如需詳細資訊,請參閱 如何設定UWP C++ app 以顯示 DirectX 檢視

當應用程式執行時引發這些事件時,處理程式會更新我們私用字段中定義的相機控制器狀態資訊。

首先,讓我們設定觸控指標事件處理程式。 在第一個事件處理程式中,OnPointerPressed,我們會從 CoreWindow 取得指標的 x-y 座標,以在使用者觸碰螢幕或按鼠時管理我們的顯示。

按下指針時

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

我們使用此處理程式來讓當前的 CameraPanController 實例知道,應將相機控制器視為作用中,方法是把 m_panInUse 設定為 TRUE。 如此一來,當應用程式呼叫 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,並關閉相機平移運動,將指標 ID 設定為 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;
}

初始化觸控控制和控制器狀態

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

初始化

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.
        );  

祝賀! 您已在遊戲中實作一組簡單的相機平移觸控控制。