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 はポインターの一意の ID です。 このサンプルではこれを使用しませんが、コントローラーの状態クラスを特定のポインターに関連付けるのが良い方法です。
- 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 オブジェクトがあることを前提としています。 詳細については、「DirectX ビューを表示するように UWP C++ アプリを設定する方法」を参照してください。
アプリの実行中にこれらのイベントが発生すると、ハンドラーはプライベート フィールドで定義されているカメラ コントローラーの状態情報を更新します。
まず、タッチ ポインター イベント ハンドラーを設定します。 最初のイベント ハンドラーである OnPointerPressed
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;
}
}
このハンドラーを使用して、現在の CameraPanController インスタンスに、m_panInUse を TRUE に設定して、カメラ コントローラーをアクティブとして扱う必要があることを通知します。 こうすることで、アプリが Update を呼び出すと、現在の位置データを使用してビューポートが更新されます。
ユーザーが画面にタッチしたとき、または表示ウィンドウでクリック押ししたときにカメラの動きの基本値を設定したので、ユーザーが画面をドラッグするか、ボタンを押してマウスを動かすときの対処方法を決定する必要があります。
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 に設定するためです。
ポインタ解放時 の
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.
);
おめでとうございます! ゲームに簡単なカメラ パン タッチ コントロールのセットを実装しました。