遊戲的觸控控制項
瞭解如何使用 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.
);
恭喜! 您已在遊戲中實作一組簡單的相機移動瀏覽觸控控制項。