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;
}
}
이 처리기를 사용하여 현재 CameraPanController 인스턴스에 m_panInUse TRUE 로 설정하여 카메라 컨트롤러를 활성으로 처리해야 한다는 것을 알 수 있습니다. 이렇게 하면 앱이 업데이트를 호출할 때 현재 위치 데이터를 사용하여 뷰포트를 갱신합니다.
이제 사용자가 화면을 터치하거나 디스플레이 창에서 클릭 누를 때 카메라 이동에 대한 기본 값을 설정했으므로 사용자가 화면을 끌거나 단추를 누른 상태에서 마우스를 이동할 때 수행할 작업을 결정해야 합니다.
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.
);
축하합니다! 게임에 간단한 카메라 팬닝 터치 컨트롤 집합을 구현했습니다.