게임용 터치 컨트롤
DirectX로 UWP(유니버설 Windows 플랫폼) 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는 플레이어가 터치 화면에서 누른 손가락을 제거할 때 앱이 호출하는 이벤트 처리기입니다.
마지막으로 이러한 메서드와 속성을 사용하여 카메라 컨트롤러 상태 정보를 초기화, 액세스, 업데이트합니다.
- 초기화는 앱이 컨트롤을 초기화하고 표시 창을 설명하는 CoreWindow 개체에 연결하기 위해 호출하는 이벤트 처리기입니다.
- SetPosition은 앱이 장면 공간에서 컨트롤의 (x, y, z) 좌표를 설정하기 위해 호출하는 메서드입니다. 이 자습서 전체에서 z 좌표는 0이라는 점을 참고하세요.
- get_Position은 장면 공간에서 카메라의 현재 위치를 가져오기 위해 앱에서 액세스하는 속성입니다. 현재 카메라 위치를 앱으로 전달하는 방식으로 이 속성을 사용합니다.
- get_FixedLookPoint는 컨트롤러 카메라가 향하고 있는 현재 지점을 가져오기 위해 앱에서 액세스하는 속성입니다. 이 예제에서는 x-y 평면으로 정상 잠깁니다.
- 업데이트는 컨트롤러 상태를 읽고 카메라 위치를 업데이트하는 방법입니다. 앱의 기본 루프에서 <이것>을 지속적으로 호출하여 카메라 컨트롤러 데이터와 장면 공간의 카메라 위치를 새로 고칩니다.
이제 터치 컨트롤을 구현하는 데 필요한 모든 구성 요소가 여기에 있습니다. 터치 또는 마우스 포인터 이벤트가 발생한 시기, 위치, 동작을 감지할 수 있습니다. 장면 공간을 기준으로 카메라의 위치와 방향을 설정하고 변경 내용을 추적할 수 있습니다. 마지막으로 호출 앱에 새 카메라 위치를 전달할 수 있습니다.
이제 이를 함께 연결해 보겠습니다.
기본 터치 이벤트 생성
Windows 런타임 이벤트 디스패처는 앱에서 처리할 3개의 이벤트를 제공합니다.
이러한 이벤트는 CoreWindow 형식에서 구현됩니다. 사용할 CoreWindow 개체가 있다고 가정합니다. 자세한 내용은 DirectX 보기를 표시하도록 UWP C++ 앱을 설정하는 방법을 참조하세요.
앱이 실행되는 동안 이러한 이벤트가 발생함에 따라 처리기는 전용 필드에 정의된 카메라 컨트롤러의 상태 정보를 업데이트합니다.
먼저 터치 포인터 이벤트 처리기를 채웁니다. 첫 번째 이벤트 처리기 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 인스턴스에 알립니다. 이렇게 하면 앱이 업데이트를 호출할 때 현재 위치 데이터를 사용하여 뷰포트를 업데이트합니다.
이제 사용자가 화면을 터치하거나 디스플레이 창에서 클릭 누를 때 카메라 이동에 대한 기본 값을 설정했으므로 사용자가 화면을 끌거나 버튼을 누른 상태에서 마우스를 이동할 때 수행할 작업을 결정해야 합니다.
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;
}
마지막으로 플레이어가 화면 터치를 중지할 때 카메라 팬 동작을 비활성화해야 합니다. PointerReleased가 발생할 때 호출되는 OnPointerReleased를 사용하여 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 );
}
초기화는 앱의 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.
);
축하합니다! 게임에서 간단한 카메라 이동 터치 컨트롤 집합을 구현했습니다.