Compartilhar via


controles de movimento e visualização para jogos

Saiba como adicionar controles tradicionais de movimentação e visualização usando mouse e teclado (também conhecidos como controles de visualização por mouse) ao seu jogo com DirectX.

Também discutimos o suporte ao recurso de controle move-look para dispositivos touch, com o controlador de movimento definido como a seção inferior esquerda da tela que se comporta como uma entrada direcional, e o controlador de visão definido para o restante da tela, com a câmera centralizando no último local em que o jogador tocou nessa área.

Se esse for um conceito de controle desconhecido para você, pense dessa forma: o teclado (ou o painel de entrada direcional por toque) controla suas pernas neste espaço 3D e se comporta como se suas pernas só fossem capazes de se mover para frente ou para trás, ou se mover para a esquerda ou para a direita. O mouse (ou ponteiro de toque) controla sua cabeça. Você usa sua cabeça para olhar em uma direção - esquerda ou direita, para cima ou para baixo, ou em algum lugar naquele avião. Se houver um destino em sua exibição, você usaria o mouse para centralizar o modo de exibição da câmera nesse destino e pressionar a tecla para frente para se mover em direção a ele ou voltar para se afastar dele. Para circular o alvo, você manteria a exibição da câmera centralizada no alvo e se moveria para a esquerda ou para a direita ao mesmo tempo. Você pode ver como esse é um método de controle muito eficaz para navegar em ambientes 3D!

Esses controles são comumente conhecidos como controles WASD em jogos, onde as teclas W, A, S e D são usadas para movimento fixo de câmera do plano x-z, e o mouse é usado para controlar a rotação da câmera em torno dos eixos x e y.

Objectivos

  • Adicione controles básicos de movimento e visão ao seu jogo DirectX para mouse, teclado e telas sensíveis ao toque.
  • Implemente uma câmera em primeira pessoa usada para navegar em um ambiente 3D.

Uma observação sobre implementações de controle de toque

Para controles de toque, implementamos dois controladores: o controlador de movimento, que manipula o movimento no plano x-z em relação ao ponto de visão da câmera; e o controlador de olhar, que mira o ponto de visão da câmera. Nosso controle de movimento é mapeado para os botões WASD do teclado e o controle de visão é mapeado para o mouse. Mas, para controles de toque, precisamos definir uma região da tela que serve como entradas direcionais ou os botões WASD virtuais, com o restante da tela servindo como espaço de entrada para os controles de aparência.

Nossa tela tem esta aparência.

o layout do controlador de mover-olhar

Quando você move o ponteiro de toque (não o mouse!) no canto inferior esquerdo da tela, qualquer movimento para cima fará com que a câmera avance. Qualquer movimento para baixo fará com que a câmera se mova para trás. O mesmo acontece para o movimento para a esquerda e para a direita dentro do espaço de ponteiro do controlador de movimento. Fora desse espaço, ele se torna um controlador de visualização - você apenas toca ou arrasta a câmera para onde gostaria que ela ficasse voltada.

Configurar a infraestrutura básica de eventos de entrada

Primeiro, devemos criar nossa classe de controle que usamos para lidar com eventos de entrada do mouse e do teclado e atualizar a perspectiva da câmera com base nessa entrada. Como estamos implementando controles de movimento e visualização, chamamos de 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

Agora, vamos criar um cabeçalho que define o estado do controlador de aparência de movimento e sua câmera em primeira pessoa, além dos métodos básicos e manipuladores de eventos que implementam os controles e que atualizam o estado da câmera.

#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

Nosso código contém quatro grupos de campos privados. Vamos examinar a finalidade de cada um deles.

Primeiro, definimos alguns campos úteis que armazenam informações atualizadas sobre a visualização da nossa câmera.

  • m_position é a posição da câmera (e, portanto, o plano de exibição) na cena 3D, usando coordenadas de cena.
  • m_pitch é a inclinação da câmera, ou seu movimento de rotação para cima e para baixo em torno do eixo x do plano de visão, em radianos.
  • m_yaw é a direção de guinada da câmera, ou sua rotação esquerda-direita em torno do eixo y do plano de visão, em radianos.

Agora, vamos definir os campos que usamos para armazenar informações sobre o status e a posição de nossos controladores. Primeiro, definiremos os campos necessários para nosso controlador de movimento baseado em toque. (Não é necessário nada de especial para implementar o controle de movimentação pelo teclado. Simplesmente lemos os eventos de teclado com manipuladores específicos.)

  • m_moveInUse indica se o controlador de movimentação está em uso.
  • m_movePointerID é o identificador exclusivo do ponteiro de movimento atual. Usamos isso para diferenciar entre o ponteiro de olhar e o ponteiro de movimentação quando verificamos o valor da ID do ponteiro.
  • m_moveFirstDown é o ponto na tela em que o jogador tocou pela primeira vez na área do ponteiro do controlador de movimento. Usamos esse valor mais tarde para definir uma zona morta para impedir que pequenos movimentos tremam a exibição.
  • m_movePointerPosition é o ponto na tela para o qual o jogador atualmente moveu o ponteiro. Usamos-o para determinar a direção que o jogador queria mover examinando-o em relação a m_moveFirstDown.
  • m_moveCommand é o comando computado final para o controlador de movimentação: para cima (para frente), para baixo (para trás), para a esquerda ou para a direita.

Agora, definimos os campos que usamos para nosso controlador de visualização, tanto para as implementações de mouse quanto de toque.

  • m_lookInUse indica se o controle de aparência está em uso.
  • m_lookPointerID é a ID exclusiva do ponteiro visual atual. Usamos isso para diferenciar entre o ponteiro de olhar e o ponteiro de movimentação quando verificamos o valor da ID do ponteiro.
  • m_lookLastPoint é o último ponto, nas coordenadas da cena, que foi capturado no quadro anterior.
  • m_lookLastDelta é a diferença computada entre o m_position atual e o m_lookLastPoint.

Por fim, definimos 6 valores boolianos para os 6 graus de movimento, que usamos para indicar o estado atual de cada ação de movimentação direcional (ativa ou desativada):

  • m_forward, m_back, m_left, m_right, m_up e m_down.

Usamos os 6 manipuladores de eventos para capturar os dados de entrada que usamos para atualizar o estado de nossos controladores:

  • OnPointerPressed. O jogador pressionou o botão esquerdo do mouse com o ponteiro na tela do jogo ou tocou na tela.
  • onPointerMoved. O jogador moveu o mouse com o ponteiro na tela do jogo ou arrastou o ponteiro de toque na tela.
  • OnPointerReleased. O jogador liberou o botão esquerdo do mouse com o ponteiro na tela do jogo ou parou de tocar na tela.
  • OnKeyDown. O jogador pressionou uma tecla.
  • OnKeyUp. O jogador liberou uma chave.

Por fim, usamos esses métodos e propriedades para inicializar, acessar e atualizar as informações de estado dos controladores.

  • Inicializar. Nosso aplicativo chama esse manipulador de eventos para inicializar os controles e anexá-los ao objeto CoreWindow que descreve nossa janela de exibição.
  • DefinirPosição. Nosso aplicativo chama esse método para definir as coordenadas (x, y e z) de nossos controles no espaço de cena.
  • SetOrientation. Nosso aplicativo chama esse método para definir a inclinação e a guinada da câmera.
  • get_Position. Nosso aplicativo acessa essa propriedade para obter a posição atual da câmera no espaço da cena. Você usa essa propriedade como o método de comunicação da posição da câmera atual para o aplicativo.
  • get_LookPoint. Nosso aplicativo acessa essa propriedade para obter o ponto atual para o qual a câmera do controlador está voltada.
  • Atualizar. Lê o estado dos controladores de movimentação e visualização e atualiza a posição da câmera. Você chama continuamente esse método do loop principal do aplicativo para atualizar os dados do controlador de câmera e a posição da câmera no espaço da cena.

Agora, você tem aqui todos os componentes necessários para implementar seus controles de movimento e visão. Então, vamos conectar essas peças.

Criar os eventos de entrada básicos

O dispatcher de eventos do Windows Runtime fornece 5 eventos que queremos que as instâncias da classe MoveLookController manipulem:

Esses eventos são implementados no tipo CoreWindow. Presumimos que você tenha um objeto CoreWindow com o qual trabalhar. Se você não souber como obter um, consulte Como configurar seu aplicativo C++ da Plataforma Universal do Windows (UWP) para exibir uma exibição do DirectX.

À medida que esses eventos são acionados enquanto nosso aplicativo está em execução, os manipuladores atualizam as informações de estado dos controladores definidas em nossos campos privados.

Primeiro, vamos configurar os manipuladores de eventos do mouse e do ponteiro de toque. No primeiro manipulador de eventos, OnPointerPressed(), obtemos as coordenadas x-y do ponteiro do CoreWindow que gerencia nossa exibição quando o usuário clica no mouse ou toca na tela na área do controlador de visualização.

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

Esse manipulador de eventos verifica se o ponteiro não é o mouse (para fins deste exemplo, que dá suporte ao mouse e ao toque) e se ele está na área do controlador de movimentação. Se ambos os critérios forem verdadeiros, ele verifica se o ponteiro foi pressionado recentemente, especificamente, se este clique não está relacionado a uma entrada de movimento ou visual anterior, testando se m_moveInUse é falso. Nesse caso, o manipulador captura o ponto na área do controlador de movimento onde a pressão aconteceu e define m_moveInUse como true, para que, quando esse manipulador for chamado novamente, ele não substituirá a posição inicial da interação de entrada do controlador. Ele também atualiza a ID do ponteiro do controlador de movimentação para a ID do ponteiro atual.

Se o ponteiro for o mouse ou se o ponteiro de toque não estiver na área do controlador de movimento, ele deverá estar na área do controlador de visão. Ele define m_lookLastPoint para a posição atual em que o usuário pressionou o botão do mouse ou tocou e manteve pressionado, redefine o delta e atualiza a ID do ponteiro do controlador de visualização para a ID do ponteiro atual. Ele também define o estado do controlador de aparência como ativo.

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

O manipulador de eventos OnPointerMoved é acionado sempre que o ponteiro se move (nesse caso, se um ponteiro de tela sensível ao toque está sendo arrastado ou se o ponteiro do mouse está sendo movido enquanto o botão esquerdo é pressionado). Se a ID do ponteiro for a mesma que a ID do controlador de movimento, ele será o ponteiro de movimento; caso contrário, verificamos se o controlador de visualização é o ponteiro ativo.

Se for o controlador de movimentação, basta atualizar a posição do ponteiro. Continuamos atualizando-o enquanto o evento PointerMoved continuar sendo disparado, pois queremos comparar a posição final com a primeira que capturamos com o manipulador de eventos OnPointerPressed.

Se for o controlador de aparência, as coisas são um pouco mais complicadas. Precisamos calcular um novo ponto de foco e centralizar a câmera nele, então calculamos o delta entre o último ponto de foco e a posição atual da tela, e em seguida multiplicamos pelo nosso fator de escala, que podemos ajustar para tornar os movimentos de foco menores ou maiores em relação ao movimento da tela. Usando esse valor, calculamos a inclinação e a guinada.

Por fim, precisamos desativar os comportamentos do controlador de movimentação ou de olhar quando o jogador parar de mover o mouse ou tocar na tela. Usamos OnPointerReleased, que chamamos quando PointerReleased é acionado, para definir m_moveInUse ou m_lookInUse como FALSE e desativar o movimento panorâmico da câmera, e zerar o ID do ponteiro.

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

Até agora, lidamos com todos os eventos de tela sensível ao toque. Agora, vamos lidar com os principais eventos de entrada para um controlador de movimento baseado em teclado.

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

Desde que uma dessas teclas seja pressionada, esse manipulador de eventos define o estado de movimentação direcional correspondente como 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;
}

E quando a chave é liberada, esse manipulador de eventos a define como false. Quando chamamos para atualizar, ele verifica esses estados de movimentação direcional e move a câmera adequadamente. Isso é um pouco mais simples do que a implementação de toque!

Inicializar os controles de toque e o estado do controlador

Vamos conectar os eventos agora e inicializar todos os campos de estado do controlador.

inicializar

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.

}

Inicialize recebe uma referência à instância de CoreWindow do aplicativo como um parâmetro e registra os manipuladores de eventos que desenvolvemos para os eventos apropriados nesse CoreWindow. Ele inicializa as IDs do ponteiro de movimento e de visualização, define o vetor de comando para a nossa implementação do controlador de movimento da tela sensível ao toque como zero e ajusta a câmera para olhar diretamente à frente quando o aplicativo é iniciado.

Obtendo e definindo a posição e a orientação da câmera

Vamos definir alguns métodos para obter e definir a posição da câmera em relação ao visor.

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

Atualizando as informações de estado do controlador

Agora, realizamos nossos cálculos que convertem as informações de coordenadas de ponteiro rastreadas em m_movePointerPosition em novas informações de coordenadas relativas ao nosso sistema de coordenadas global. Nosso aplicativo chama esse método sempre que atualizamos o loop principal do aplicativo. Portanto, é aqui que calculamos as novas informações de posição do ponto de observação que queremos passar para a aplicação para atualizar a matriz de visualização antes da projeção na área de exibição.

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

}

Como não queremos movimento nervoso quando o jogador usa nosso controlador de movimento baseado em toque, definimos uma zona morta virtual ao redor do ponteiro com um diâmetro de 32 pixels. Também adicionamos velocidade, que é o valor do comando mais uma taxa de ganho de movimento. (Você pode ajustar esse comportamento de acordo com seu gosto, para diminuir ou acelerar a taxa de movimento com base na distância que o ponteiro move na área do controlador de movimentação.)

Quando calculamos a velocidade, também convertemos as coordenadas recebidas dos controladores de movimentação e visualização no movimento do ponto de visão real que enviamos para o método que computa nossa matriz de visualização para a cena. Primeiro, invertemos a coordenada x, pois, se clicarmos ou arrastarmos para a esquerda ou para a direita com o controlador de visão, o ponto de visão girará na direção oposta na cena, como uma câmera pode oscilar em torno do seu eixo central. Em seguida, trocamos os eixos y e z, pois uma tecla para cima/para baixo ou um movimento de arrastar por toque (lido como um comportamento de eixo y) no controlador de movimento deve se traduzir em uma ação de câmera que move o ponto de visão para dentro ou para fora da tela (o eixo z).

A posição final do ponto de vista do jogador é a última posição mais a velocidade calculada, e é isso que é lido pelo renderizador quando ele chama o método get_Position (provavelmente durante a configuração de cada quadro). Depois disso, redefinimos o comando de movimentação para zero.

Atualizando a matriz de exibição com a nova posição da câmera

Podemos obter uma coordenada de espaço de cena na qual nossa câmera está focada e que é atualizada sempre que você diz ao aplicativo para fazer isso (a cada 60 segundos no loop principal do aplicativo, por exemplo). Este pseudocódigo sugere o comportamento de chamada que você pode implementar:

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

Parabéns! Você implementou controles básicos de aparência de movimento para telas sensíveis ao toque e controles de toque de entrada de teclado/mouse em seu jogo!