다음을 통해 공유


게임의 이동 및 시야 조작

DirectX 게임에 기존 마우스 및 키보드 이동 모양 컨트롤(mouselook 컨트롤이라고도 함)을 추가하는 방법을 알아봅니다.

또한 터치 장치에 대한 이동-보기 지원에 대해 논의합니다. 화면의 왼쪽 아래 부분이 방향 입력처럼 동작하는 이동 컨트롤러로 정의되고, 나머지 화면에서는 플레이어가 마지막으로 터치한 지점을 중심으로 카메라가 움직이는 보기 컨트롤러로 정의됩니다.

이것이 익숙하지 않은 컨트롤 개념인 경우, 이렇게 생각해 보십시오: 이 3D 공간에서 키보드(또는 터치 기반 방향 입력 상자)는 다리를 제어하여, 마치 다리가 앞으로 또는 뒤로만 이동하거나, 왼쪽과 오른쪽으로 살짝 걸을 수 있는 것처럼 작동합니다. 마우스(또는 터치 포인터)가 머리를 제어합니다. 머리를 사용하여 왼쪽이나 오른쪽, 위쪽 또는 아래쪽 또는 그 비행기의 어딘가에 있는 방향을 볼 수 있습니다. 화면에 대상이 보이는 경우, 마우스를 사용하여 카메라 뷰를 해당 대상의 중앙에 맞춘 다음, 앞으로 가기 키를 눌러 대상 쪽으로 이동하거나 뒤로 가기 키를 눌러 대상에서 멀어집니다. 대상에 동그라미를 그리려면 카메라 보기를 대상의 가운데에 두고 동시에 왼쪽 또는 오른쪽으로 이동합니다. 이것이 3D 환경을 탐색하는 매우 효과적인 제어 방법인지 확인할 수 있습니다.

이러한 컨트롤은 일반적으로 게임에서 WASD 컨트롤이라고 하며, 여기서 W, A, S 및 D 키는 x-z 평면 고정 카메라 이동에 사용되고 마우스는 x축과 y축 주위의 카메라 회전을 제어하는 데 사용됩니다.

목표

  • 마우스와 키보드, 터치 스크린 모두에 대한 DirectX 게임에 기본 이동 모양 컨트롤을 추가합니다.
  • 3D 환경을 탐색하는 데 사용되는 1인칭 카메라를 구현합니다.

터치 컨트롤 구현에 대한 참고 사항

터치 컨트롤의 경우, 우리는 두 가지 컨트롤러를 구현합니다. 첫 번째는 카메라의 시점 기준으로 x-z 평면에서 움직임을 조작하는 이동 컨트롤러이며, 두 번째는 카메라의 시점을 조정하는 보기 컨트롤러입니다. 이동 컨트롤러는 키보드 WASD 단추에 매핑되고 보기 컨트롤러는 마우스에 매핑됩니다. 그러나 터치 컨트롤의 경우 방향 입력 또는 가상 WASD 단추 역할을 하는 화면 영역을 정의해야 하며 나머지 화면은 보기 컨트롤의 입력 공간 역할을 합니다.

화면은 다음과 같습니다.

이동 모양 컨트롤러 레이아웃

화면 왼쪽 아래에 있는 터치 포인터(마우스 아님)를 이동하면 위쪽으로 이동하면 카메라가 앞으로 이동합니다. 아래쪽으로 이동하면 카메라가 뒤로 이동합니다. 이동 컨트롤러의 포인터 공간 내에서 왼쪽 및 오른쪽 이동에 대해서도 동일한 원칙이 적용됩니다. 그 공간 밖에 있으면, 카메라 컨트롤러로 변합니다. 원하는 방향으로 카메라를 터치하거나 드래그하세요.

기본 입력 이벤트 인프라 설정

먼저 마우스 및 키보드의 입력 이벤트를 처리하기 위해 컨트롤 클래스를 생성하고, 그 입력에 따라 카메라 관점을 업데이트해야 합니다. 무브-룩 조작을 구현하기 때문에 저희는 MoveLookController이라고 부릅니다.

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 MoveLookController
{
};  // class MoveLookController

이제 이동-보기 컨트롤러 및 1인칭 카메라의 상태와 컨트롤을 구현하고 카메라의 상태를 업데이트하는 기본 메서드 및 이벤트 처리기를 정의하는 헤더를 만들어 보겠습니다.

#define ROTATION_GAIN 0.004f    // Sensitivity adjustment for the look controller
#define MOVEMENT_GAIN 0.1f      // Sensitivity adjustment for the move controller

ref class MoveLookController
{
private:
    // Properties of the controller object
    DirectX::XMFLOAT3 m_position;               // The position of the controller
    float m_pitch, m_yaw;           // Orientation euler angles in radians

    // Properties of the Move control
    bool m_moveInUse;               // Specifies whether the move control is in use
    uint32 m_movePointerID;         // Id of the pointer in this control
    DirectX::XMFLOAT2 m_moveFirstDown;          // Point where initial contact occurred
    DirectX::XMFLOAT2 m_movePointerPosition;   // Point where the move pointer is currently located
    DirectX::XMFLOAT3 m_moveCommand;            // The net command from the move control

    // Properties of the Look control
    bool m_lookInUse;               // Specifies whether the look control is in use
    uint32 m_lookPointerID;         // Id of the pointer in this control
    DirectX::XMFLOAT2 m_lookLastPoint;          // Last point (from last frame)
    DirectX::XMFLOAT2 m_lookLastDelta;          // For smoothing

    bool m_forward, m_back;         // States for movement
    bool m_left, m_right;
    bool m_up, m_down;


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

    void OnKeyDown(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::KeyEventArgs^ args
        );

    void OnKeyUp(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::KeyEventArgs^ args
        );

    // Set up the Controls that this controller supports
    void Initialize( _In_ Windows::UI::Core::CoreWindow^ window );

    void Update( Windows::UI::Core::CoreWindow ^window );
    
internal:
    // Accessor to set position of controller
    void SetPosition( _In_ DirectX::XMFLOAT3 pos );

    // Accessor to set position of controller
    void SetOrientation( _In_ float pitch, _In_ float yaw );

    // Returns the position of the controller object
    DirectX::XMFLOAT3 get_Position();

    // Returns the point  which the controller is facing
    DirectX::XMFLOAT3 get_LookPoint();


};  // class MoveLookController

코드에는 4개의 프라이빗 필드 그룹이 포함되어 있습니다. 각 용도를 검토해 보겠습니다.

먼저 카메라 보기에 대한 업데이트된 정보를 포함하는 몇 가지 유용한 필드를 정의합니다.

  • m_position는 3D 장면에서 장면 좌표계를 사용하여 카메라(그리고 뷰플레인)의 위치입니다.
  • m_pitch는 카메라의 피치로, 이것은 뷰플레인의 x축을 중심으로 상하로 회전하는 것을 라디안 단위로 나타냅니다.
  • m_yaw는 카메라의 요(yaw), 즉 뷰플레인의 y축을 중심으로 한 좌우 회전 각도(라디안 단위)입니다.

이제 컨트롤러의 상태 및 위치에 대한 정보를 저장하는 데 사용하는 필드를 정의해 보겠습니다. 먼저 터치 기반 이동 컨트롤러에 필요한 필드를 정의합니다. (이동 컨트롤러의 키보드 구현에 특별히 필요한 것은 없습니다. 특정 처리기를 사용하여 키보드 이벤트를 읽습니다.)

  • m_moveInUse 이동 컨트롤러가 사용 중인지 여부를 나타냅니다.
  • m_movePointerID 현재 이동 포인터의 고유 ID입니다. 포인터 ID 값을 확인할 때 보기 포인터와 이동 포인터를 구분하는 데 사용합니다.
  • m_moveFirstDown 플레이어가 이동 컨트롤러 포인터 영역을 처음 터치한 화면의 지점입니다. 나중에 이 값을 사용하여 작은 움직임으로 인해 뷰가 흔들리지 않도록 무감도 영역을 설정합니다.
  • m_movePointerPosition 플레이어가 현재 포인터를 이동한 화면의 지점입니다. 이를 사용하여 플레이어가 이동하고자 하는 방향을 m_moveFirstDown기준으로 검사하여 결정합니다.
  • m_moveCommand 이동 컨트롤러에 대한 최종 계산 명령입니다. 위쪽(앞으로), 아래쪽(뒤로), 왼쪽 또는 오른쪽입니다.

이제 보기 컨트롤러에 사용하는 필드(마우스 및 터치 구현 모두)를 정의합니다.

  • m_lookInUse 보기 컨트롤이 사용 중인지 여부를 나타냅니다.
  • m_lookPointerID 현재 모양 포인터의 고유 ID입니다. 포인터 ID 값을 확인할 때 보기 포인터와 이동 포인터를 구분하는 데 사용합니다.
  • m_lookLastPoint 이전 프레임에서 캡처된 장면 좌표의 마지막 지점입니다.
  • m_lookLastDelta는 현재 m_positionm_lookLastPoint사이의 계산된 차이입니다.

마지막으로, 각 방향 이동 동작의 현재 상태를 나타내는 데 사용하는 6도 이동에 대해 6개의 부울 값을 정의합니다(켜기 또는 끄기).

  • m_forward, m_back, m_left, m_right, m_upm_down.

6개의 이벤트 처리기를 사용하여 컨트롤러의 상태를 업데이트하는 데 사용하는 입력 데이터를 캡처합니다.

  • onPointerPressed. 플레이어가 게임 화면에서 포인터를 사용하여 마우스 왼쪽 단추를 누르거나 화면을 터치했습니다.
  • onPointerMoved. 플레이어가 게임 화면에서 포인터를 사용하여 마우스를 이동하거나 화면에서 터치 포인터를 끌어갔습니다.
  • OnPointerReleased. 플레이어가 게임 화면에 포인터가 있는 왼쪽 마우스 단추를 놓거나 화면 터치를 중지했습니다.
  • OnKeyDown. 플레이어가 키를 눌렀습니다.
  • OnKeyUp. 플레이어가 키를 놓았습니다.

마지막으로 이러한 메서드와 속성을 사용하여 컨트롤러의 상태 정보를 초기화, 액세스 및 업데이트합니다.

  • 를 초기화합니다. 앱은 이 이벤트 처리기를 호출하여 컨트롤을 초기화하고 표시 창을 설명하는 CoreWindow 개체에 연결합니다.
  • SetPosition. 앱은 이 메서드를 호출하여 장면 공간에서 컨트롤의 좌표(x, y 및 z)를 설정합니다.
  • SetOrientation. 우리 앱은 이 메서드를 호출하여 카메라의 피치와 요를 설정합니다.
  • get_Position. 앱은 장면 공간에서 카메라의 현재 위치를 가져오기 위해 이 속성에 액세스합니다. 현재 카메라 위치를 앱에 전달하는 방법으로 이 속성을 사용합니다.
  • get_LookPoint. 앱은 컨트롤러 카메라가 마주보고 있는 현재 지점을 가져오기 위해 이 속성에 액세스합니다.
  • 업데이트. 이동 상태를 읽고 컨트롤러를 보고 카메라 위치를 업데이트합니다. 앱의 주 루프에서 이 메서드를 지속적으로 호출하여 장면 공간에서 카메라 컨트롤러 데이터와 카메라 위치를 새로 고칩니다.

이제 이동 모양 컨트롤을 구현하는 데 필요한 모든 구성 요소가 여기에 있습니다. 따라서 이러한 부분을 함께 연결해 보겠습니다.

기본 입력 이벤트 만들기

Windows 런타임 이벤트 디스패처는 MoveLookController 클래스의 인스턴스가 처리할 5개의 이벤트를 제공합니다.

이러한 이벤트는 CoreWindow 형식에서 구현됩니다. 사용 가능한 CoreWindow 개체가 있다고 가정합니다. 가져오는 방법을 모르는 경우 UWP(유니버설 Windows 플랫폼) C++ 앱을 설정하여 DirectX 보기표시하는 방법을 참조하세요.

앱이 실행되는 동안 이러한 이벤트가 발생함에 따라 처리기는 프라이빗 필드에 정의된 컨트롤러의 상태 정보를 업데이트합니다.

먼저 마우스 및 터치 포인터 이벤트 처리기를 채우겠습니다. 첫 번째 이벤트 처리기인 onPointerPressed()사용자가 마우스를 클릭하거나 보기 컨트롤러 영역에서 화면을 터치할 때 디스플레이를 관리하는 CoreWindow 포인터의 x-y 좌표를 가져옵니다.

onPointerPressed

void MoveLookController::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 ( deviceType == PointerDeviceType::Mouse )
    {
        // Action, Jump, or Fire
    }

    // Check  if this pointer is in the move control.
    // Change the values  to percentages of the preferred screen resolution.
    // You can set the x value to <preferred resolution> * <percentage of width>
    // for example, ( position.x < (screenResolution.x * 0.15) ).

    if (( position.x < 300 && position.y > 380 ) && ( deviceType != PointerDeviceType::Mouse ))
    {
        if ( !m_moveInUse ) // if no pointer is in this control yet
        {
            // Process a DPad touch down event.
            m_moveFirstDown = position;                 // Save the location of the initial contact.
            m_movePointerPosition = position;
            m_movePointerID = pointerID;                // Store the id of the pointer using this control.
            m_moveInUse = TRUE;
        }
    }
    else // This pointer must be in the look control.
    {
        if ( !m_lookInUse ) // If no pointer is in this control yet...
        {
            m_lookLastPoint = position;                         // save the point for later move
            m_lookPointerID = args->CurrentPoint->PointerId;  // store the id of pointer using this control
            m_lookLastDelta.x = m_lookLastDelta.y = 0;          // these are for smoothing
            m_lookInUse = TRUE;
        }
    }
}

이 이벤트 처리기는 포인터가 마우스가 아닌지(마우스와 터치를 모두 지원하는 이 샘플의 용도로) 및 이동 컨트롤러 영역에 있는지 확인합니다. 두 기준이 모두 참이면, 포인터가 방금 눌렸는지, 특히 이 클릭이 이전의 이동이나 보기 입력과 관련이 없는지를 확인하기 위해 "m_moveInUse"가 false인지 테스트합니다. 이 경우 처리기는 누름이 발생한 이동 컨트롤러 영역의 지점을 캡처하고 m_moveInUse true로 설정하므로 이 처리기가 다시 호출될 때 이동 컨트롤러 입력 상호 작용의 시작 위치를 덮어쓰지 않습니다. 또한 이동 컨트롤러 포인터 ID를 현재 포인터의 ID로 업데이트합니다.

포인터가 마우스이거나 터치 포인터가 이동 컨트롤러 영역에 없는 경우 보기 컨트롤러 영역에 있어야 합니다. m_lookLastPoint을 사용자가 마우스 버튼을 누르거나 터치하여 누른 현재 위치로 설정하고, 델타를 초기화하며, 보기 컨트롤러의 포인터 ID를 현재 포인터 ID로 업데이트합니다. 또한 보기 컨트롤러의 상태를 활성으로 설정합니다.

onPointerMoved

void MoveLookController::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);

    // Decide which control this pointer is operating.
    if (pointerID == m_movePointerID)           // This is the move pointer.
    {
        // Move control
        m_movePointerPosition = position;       // Save the current position.

    }
    else if (pointerID == m_lookPointerID)      // This is the look pointer.
    {
        // Look control

        DirectX::XMFLOAT2 pointerDelta;
        pointerDelta.x = position.x - m_lookLastPoint.x;        // How far did pointer move
        pointerDelta.y = position.y - m_lookLastPoint.y;

        DirectX::XMFLOAT2 rotationDelta;
        rotationDelta.x = pointerDelta.x * ROTATION_GAIN;   // Scale for control sensitivity.
        rotationDelta.y = pointerDelta.y * ROTATION_GAIN;

        m_lookLastPoint = position;                     // Save for the next time through.

                                                        // Update our orientation based on the command.
        m_pitch -= rotationDelta.y;                     // Mouse y increases down, but pitch increases up.
        m_yaw -= rotationDelta.x;                       // Yaw is defined as CCW around the y-axis.

                                                        // Limit the pitch to straight up or straight down.
        m_pitch = (float)__max(-DirectX::XM_PI / 2.0f, m_pitch);
        m_pitch = (float)__min(+DirectX::XM_PI / 2.0f, m_pitch);
    }
}

OnPointerMoved 이벤트 처리기는 포인터가 이동할 때마다 발생합니다. 터치 스크린 포인터를 끌거나, 왼쪽 단추를 누른 상태에서 마우스 포인터를 이동하는 경우에 해당합니다. 포인터 ID가 이동 컨트롤러 포인터의 ID와 같으면 그것이 이동 포인터입니다. 다른 경우에는 활성 포인터가 보기 컨트롤러인지 확인합니다.

이동 컨트롤러인 경우 포인터 위치만 업데이트합니다. PointerMoved 이벤트가 계속 발생하는 동안 계속 업데이트합니다. 이는 최종 위치를 OnPointerPressed 이벤트 처리기로 캡처한 첫 번째 위치와 비교하려고 하기 때문입니다.

룩 컨트롤러인 경우 상황이 좀 더 복잡합니다. 새로운 보기 지점을 계산하고 카메라를 가운데에 배치해야 하므로 마지막 보기 지점과 현재 화면 위치 사이의 델타를 계산한 다음 배율 인수에 곱하여 화면 이동 거리를 기준으로 보기 움직임을 작거나 크게 조정할 수 있습니다. 해당 값을 사용하여 피치와 요를 계산합니다.

마지막으로 플레이어가 마우스 이동 또는 화면 터치를 중지할 때 이동 또는 보기 컨트롤러 동작을 비활성화해야 합니다. OnPointerReleased을 사용하여 PointerReleased가 발생할 때 호출하고, m_moveInUse 또는 m_lookInUse를 FALSE로 설정하며, 카메라 팬 움직임을 끄고 포인터 ID를 0으로 설정합니다.

onPointerReleased

void MoveLookController::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 );


    if ( pointerID == m_movePointerID )    // This was the move pointer.
    {
        m_moveInUse = FALSE;
        m_movePointerID = 0;
    }
    else if (pointerID == m_lookPointerID ) // This was the look pointer.
    {
        m_lookInUse = FALSE;
        m_lookPointerID = 0;
    }
}

지금까지 모든 터치 스크린 이벤트를 처리했습니다. 이제 키보드 기반 이동 컨트롤러에 대한 키 입력 이벤트를 처리해 보겠습니다.

OnKeyDown

void MoveLookController::OnKeyDown(
                                   __in CoreWindow^ sender,
                                   __in KeyEventArgs^ args )
{
    Windows::System::VirtualKey Key;
    Key = args->VirtualKey;

    // Figure out the command from the keyboard.
    if ( Key == VirtualKey::W )     // Forward
        m_forward = true;
    if ( Key == VirtualKey::S )     // Back
        m_back = true;
    if ( Key == VirtualKey::A )     // Left
        m_left = true;
    if ( Key == VirtualKey::D )     // Right
        m_right = true;
}

이러한 키 중 하나를 누르면 이 이벤트 처리기는 해당 방향 이동 상태를 true로 설정합니다.

OnKeyUp

void MoveLookController::OnKeyUp(
                                 __in CoreWindow^ sender,
                                 __in KeyEventArgs^ args)
{
    Windows::System::VirtualKey Key;
    Key = args->VirtualKey;

    // Figure out the command from the keyboard.
    if ( Key == VirtualKey::W )     // forward
        m_forward = false;
    if ( Key == VirtualKey::S )     // back
        m_back = false;
    if ( Key == VirtualKey::A )     // left
        m_left = false;
    if ( Key == VirtualKey::D )     // right
        m_right = false;
}

그리고 키가 해제되면 이 이벤트 처리기는 키를 false로 다시 설정합니다. Update를 호출할 때 이러한 방향 이동 상태를 확인하고 그에 따라 카메라를 이동합니다. 터치 구현보다 약간 더 간단합니다.

터치 컨트롤 및 컨트롤러 상태 초기화

이제 이벤트를 연결하고 모든 컨트롤러 상태 필드를 초기화하겠습니다.

초기화

void MoveLookController::Initialize( _In_ CoreWindow^ window )
{

    // Opt in to receive touch/mouse events.
    window->PointerPressed += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerPressed);

    window->PointerMoved += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerMoved);

    window->PointerReleased += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerReleased);

    window->CharacterReceived +=
    ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &MoveLookController::OnCharacterReceived);

    window->KeyDown += 
    ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &MoveLookController::OnKeyDown);

    window->KeyUp += 
    ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &MoveLookController::OnKeyUp);

    // Initialize the state of the controller.
    m_moveInUse = FALSE;                // No pointer is in the Move control.
    m_movePointerID = 0;

    m_lookInUse = FALSE;                // No pointer is in the Look control.
    m_lookPointerID = 0;

    //  Need to init this as it is reset every frame.
    m_moveCommand = DirectX::XMFLOAT3( 0.0f, 0.0f, 0.0f );

    SetOrientation( 0, 0 );             // Look straight ahead when the app starts.

}

Initialize 는 앱의 CoreWindow 인스턴스를 매개 변수로 받아들여, 개발한 이벤트 처리기를 해당 CoreWindow의 적절한 이벤트에 등록합니다. 이동 및 보기 포인터의 ID를 초기화하고, 터치 스크린 이동 컨트롤러 구현에 대한 명령 벡터를 0으로 설정하고, 앱이 시작될 때 카메라를 똑바로 바라보도록 설정합니다.

카메라의 위치 및 방향 가져오기 및 설정

뷰포트와 관련하여 카메라의 위치를 가져와서 설정하는 몇 가지 방법을 정의해 보겠습니다.

void MoveLookController::SetPosition( _In_ DirectX::XMFLOAT3 pos )
{
    m_position = pos;
}

// Accessor to set the position of the controller.
void MoveLookController::SetOrientation( _In_ float pitch, _In_ float yaw )
{
    m_pitch = pitch;
    m_yaw = yaw;
}

// Returns the position of the controller object.
DirectX::XMFLOAT3 MoveLookController::get_Position()
{
    return m_position;
}

// Returns the point at which the camera controller is facing.
DirectX::XMFLOAT3 MoveLookController::get_LookPoint()
{
    float y = sinf(m_pitch);        // Vertical
    float r = cosf(m_pitch);        // In the plane
    float z = r*cosf(m_yaw);        // Fwd-back
    float x = r*sinf(m_yaw);        // Left-right
    DirectX::XMFLOAT3 result(x,y,z);
    result.x += m_position.x;
    result.y += m_position.y;
    result.z += m_position.z;

    // Return m_position + DirectX::XMFLOAT3(x, y, z);
    return result;
}

컨트롤러 상태 정보 업데이트

이제 우리는 m_movePointerPosition에 있는 추적된 포인터 좌표 정보를 우리의 세계 좌표계에 맞는 새로운 좌표 정보로 변환하는 계산을 수행합니다. 앱은 기본 앱 루프를 새로 고칠 때마다 이 메서드를 호출합니다. 따라서 뷰포트로 프로젝션하기 전에 보기 행렬을 업데이트하기 위해 앱에 전달하려는 새로운 보기 지점 위치 정보를 계산합니다.

void MoveLookController::Update(CoreWindow ^window)
{
    // Check for input from the Move control.
    if (m_moveInUse)
    {
        DirectX::XMFLOAT2 pointerDelta(m_movePointerPosition);
        pointerDelta.x -= m_moveFirstDown.x;
        pointerDelta.y -= m_moveFirstDown.y;

        // Figure out the command from the touch-based virtual joystick.
        if (pointerDelta.x > 16.0f)      // Leave 32 pixel-wide dead spot for being still.
            m_moveCommand.x = 1.0f;
        else
            if (pointerDelta.x < -16.0f)
            m_moveCommand.x = -1.0f;

        if (pointerDelta.y > 16.0f)      // Joystick y is up, so change sign.
            m_moveCommand.y = -1.0f;
        else
            if (pointerDelta.y < -16.0f)
            m_moveCommand.y = 1.0f;
    }

    // Poll our state bits that are set by the keyboard input events.
    if (m_forward)
        m_moveCommand.y += 1.0f;
    if (m_back)
        m_moveCommand.y -= 1.0f;

    if (m_left)
        m_moveCommand.x -= 1.0f;
    if (m_right)
        m_moveCommand.x += 1.0f;

    if (m_up)
        m_moveCommand.z += 1.0f;
    if (m_down)
        m_moveCommand.z -= 1.0f;

    // Make sure that 45 degree cases are not faster.
    DirectX::XMFLOAT3 command = m_moveCommand;
    DirectX::XMVECTOR vector;
    vector = DirectX::XMLoadFloat3(&command);

    if (fabsf(command.x) > 0.1f || fabsf(command.y) > 0.1f || fabsf(command.z) > 0.1f)
    {
        vector = DirectX::XMVector3Normalize(vector);
        DirectX::XMStoreFloat3(&command, vector);
    }
    

    // Rotate command to align with our direction (world coordinates).
    DirectX::XMFLOAT3 wCommand;
    wCommand.x = command.x*cosf(m_yaw) - command.y*sinf(m_yaw);
    wCommand.y = command.x*sinf(m_yaw) + command.y*cosf(m_yaw);
    wCommand.z = command.z;

    // Scale for sensitivity adjustment.
    wCommand.x = wCommand.x * MOVEMENT_GAIN;
    wCommand.y = wCommand.y * MOVEMENT_GAIN;
    wCommand.z = wCommand.z * MOVEMENT_GAIN;

    // Our velocity is based on the command.
    // Also note that y is the up-down axis. 
    DirectX::XMFLOAT3 Velocity;
    Velocity.x = -wCommand.x;
    Velocity.z = wCommand.y;
    Velocity.y = wCommand.z;

    // Integrate
    m_position.x += Velocity.x;
    m_position.y += Velocity.y;
    m_position.z += Velocity.z;

    // Clear movement input accumulator for use during the next frame.
    m_moveCommand = DirectX::XMFLOAT3(0.0f, 0.0f, 0.0f);

}

플레이어가 터치 기반 이동 컨트롤러를 사용할 때 불안한 움직임을 원하지 않기 때문에 지름이 32픽셀인 포인터 주위에 가상 데드존을 설정합니다. 또한 명령 값에 이동 증속율을 더한 것이 속도이며, 이를 추가합니다. (이 동작을 원하는 대로 조정하여 포인터가 이동 컨트롤러 영역에서 이동하는 거리에 따라 이동 속도를 늦추거나 속도를 높일 수 있습니다.)

속도를 계산할 때, 이동 및 보기 컨트롤러로부터 받은 좌표를 실제 보기 지점의 이동으로 변환하여, 장면에 대한 뷰 매트릭스를 계산하는 메서드에 전달합니다. 먼저 x 좌표를 반전합니다. 왜냐하면, 보기 컨트롤러로 클릭 이동하거나 왼쪽이나 오른쪽으로 드래그할 때, 카메라가 중앙 축을 중심으로 회전하듯이 보기 포인트가 장면에서 반대 방향으로 회전하기 때문입니다. 그런 다음 이동 컨트롤러의 위쪽/아래쪽 키 누르기 또는 터치 끌기 동작(y축 동작으로 읽기)이 화면 내부 또는 외부로 보기 지점을 이동하는 카메라 동작(z축)으로 변환되어야 하므로 y축과 z축을 교환합니다.

최종적으로 플레이어의 보기 지점 위치는 마지막 위치에 계산된 속도를 더한 값이고, 이는 렌더러가 get_Position 메서드를 호출할 때(대개 각 프레임을 설정할 때) 참조하여 읽게 됩니다. 그런 다음 move 명령을 0으로 다시 설정합니다.

새 카메라 위치로 뷰 매트릭스 업데이트

카메라가 초점을 맞추고 있는 장면 공간 좌표를 얻을 수 있으며, 이는 앱에 이 지시를 할 때마다 업데이트됩니다(예: 기본 앱 루프에서 60초마다). 이 슈도코드는 구현할 수 있는 호출 행동을 제안합니다.

myMoveLookController->Update( m_window );   

// Update the view matrix based on the camera position.
myFirstPersonCamera->SetViewParameters(
                 myMoveLookController->get_Position(),       // Point we are at
                 myMoveLookController->get_LookPoint(),      // Point to look towards
                 DirectX::XMFLOAT3( 0, 1, 0 )                   // Up-vector
                 ); 

축하합니다! 게임에서 터치 스크린과 키보드/마우스 입력 터치 컨트롤 모두에 대한 기본 이동 모양 컨트롤을 구현했습니다.