Compartir a través de


Controles de búsqueda de movimiento para juegos

Aprende a agregar controles tradicionales de movimiento de mouse y teclado (también conocidos como controles mouselook) a tu juego DirectX.

También analizamos la compatibilidad con el movimiento de los dispositivos táctiles, con el controlador de movimiento definido como la sección inferior izquierda de la pantalla que se comporta como una entrada direccional, y el controlador de aspecto definido para el resto de la pantalla, con la cámara centrada en el último lugar en el que el jugador tocó en esa área.

Si esto es un concepto de control desconocido para ti, piensa en esto: el teclado (o el cuadro de entrada direccional basado en toques) controla las piernas en este espacio 3D y se comporta como si las piernas solo fueran capaces de avanzar o retroceder, o estrafing izquierda y derecha. El mouse (o puntero táctil) controla la cabeza. Usa la cabeza para mirar en una dirección: izquierda o derecha, arriba o abajo, o en algún lugar de ese plano. Si hay un destino en la vista, usaría el mouse para centrar la vista de la cámara en ese destino y, a continuación, presionar la tecla hacia delante para desplazarse hacia ella o volver a alejarse de ella. Para rodear el destino, mantendría la vista de cámara centrada en el destino y movería hacia la izquierda o derecha al mismo tiempo. Puede ver cómo se trata de un método de control muy eficaz para navegar por entornos 3D.

Estos controles se conocen comúnmente como controles WASD en juegos, donde las teclas W, A, S y D se usan para el movimiento fijo de la cámara del plano x-z, y el mouse se usa para controlar el giro de la cámara alrededor de los ejes x e y.

Objetivos

  • Agrega controles básicos de movimiento a tu juego DirectX tanto para el mouse como para el teclado y las pantallas táctiles.
  • Implemente una cámara de primera persona que se usa para navegar por un entorno 3D.

Una nota sobre las implementaciones de control táctil

Para los controles táctiles, implementamos dos controladores: el controlador de movimiento, que controla el movimiento en el plano x-z con respecto al punto de mirada de la cámara; y el controlador de aspecto, que tiene como objetivo el punto de mirada de la cámara. Nuestro controlador de movimiento se asigna a los botones WASD del teclado y el controlador de aspecto se asigna al mouse. Pero para los controles táctiles, es necesario definir una región de la pantalla que actúa como entradas direccionales o los botones WASD virtuales, con el resto de la pantalla que actúa como espacio de entrada para los controles de apariencia.

Nuestra pantalla tiene este aspecto.

el diseño del controlador move-look

Al mover el puntero táctil (no el mouse)) en la parte inferior izquierda de la pantalla, cualquier movimiento hacia arriba hará que la cámara avance. Cualquier movimiento hacia abajo hará que la cámara se mueva hacia atrás. Lo mismo se mantiene para el movimiento izquierdo y derecho dentro del espacio de puntero del controlador de movimiento. Fuera de ese espacio, y se convierte en un controlador de aspecto, solo tiene que tocar o arrastrar la cámara a donde le gustaría que se enfrente.

Configuración de la infraestructura de eventos de entrada básica

En primer lugar, debemos crear nuestra clase de control que usamos para controlar los eventos de entrada desde el mouse y el teclado, y actualizar la perspectiva de la cámara en función de esa entrada. Dado que estamos implementando controles move-look, lo llamamos 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

Ahora, vamos a crear un encabezado que defina el estado del controlador move-look y su cámara de primera persona, además de los métodos básicos y controladores de eventos que implementan los controles y que actualizan el estado de la cámara.

#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

Nuestro código contiene 4 grupos de campos privados. Vamos a revisar el propósito de cada uno.

En primer lugar, definimos algunos campos útiles que contienen nuestra información actualizada sobre nuestra vista de cámara.

  • m_position es la posición de la cámara (y, por tanto, el plano de vista) en la escena 3D, usando coordenadas de escena.
  • m_pitch es el tono de la cámara, o su giro hacia abajo alrededor del eje X del plano de vista, en radianes.
  • m_yaw es la guiñada de la cámara, o su giro de izquierda derecha alrededor del eje Y del plano de vista, en radianes.

Ahora, vamos a definir los campos que usamos para almacenar información sobre el estado y la posición de nuestros controladores. En primer lugar, definiremos los campos que necesitamos para nuestro controlador de movimiento táctil. (No hay nada especial necesario para la implementación del teclado del controlador de movimiento. Solo se leen los eventos de teclado con controladores específicos).

  • m_moveInUse indica si el controlador de movimiento está en uso.
  • m_movePointerID es el identificador único del puntero de movimiento actual. Lo usamos para diferenciar entre el puntero de búsqueda y el puntero de movimiento cuando se comprueba el valor del identificador de puntero.
  • m_moveFirstDown es el punto en la pantalla donde el jugador tocó por primera vez el área del puntero del controlador de movimiento. Usamos este valor más adelante para establecer una zona muerta para evitar que los movimientos diminutos se activen en la vista.
  • m_movePointerPosition es el punto en la pantalla al que el jugador ha movido actualmente el puntero. Lo usamos para determinar la dirección que el jugador quería mover examinando con respecto a m_moveFirstDown.
  • m_moveCommand es el comando calculado final para el controlador de movimiento: arriba (adelante), abajo (atrás), izquierda o derecha.

Ahora, definimos los campos que usamos para nuestro controlador de apariencia, tanto el mouse como las implementaciones táctiles.

  • m_lookInUse indica si el control de apariencia está en uso.
  • m_lookPointerID es el identificador único del puntero de búsqueda actual. Lo usamos para diferenciar entre el puntero de búsqueda y el puntero de movimiento cuando se comprueba el valor del identificador de puntero.
  • m_lookLastPoint es el último punto, en coordenadas de escena, que se capturó en el fotograma anterior.
  • m_lookLastDelta es la diferencia calculada entre el m_position actual y el m_lookLastPoint.

Por último, definimos 6 valores booleanos para los 6 grados de movimiento, que usamos para indicar el estado actual de cada acción de movimiento direccional (activado o desactivado):

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

Usamos los 6 controladores de eventos para capturar los datos de entrada que usamos para actualizar el estado de nuestros controladores:

  • OnPointerPressed. El jugador presionó el botón izquierdo del mouse con el puntero en nuestra pantalla de juego o tocó la pantalla.
  • OnPointerMoved. El jugador movió el mouse con el puntero en nuestra pantalla de juego o arrastró el puntero táctil en la pantalla.
  • OnPointerReleased. El jugador lanzó el botón izquierdo del mouse con el puntero en nuestra pantalla de juego, o dejó de tocar la pantalla.
  • OnKeyDown. El jugador presionó una tecla.
  • OnKeyUp. El jugador lanzó una tecla.

Por último, usamos estos métodos y propiedades para inicializar, acceder y actualizar la información de estado de los controladores.

  • Inicializar. Nuestra aplicación llama a este controlador de eventos para inicializar los controles y adjuntarlos al objeto CoreWindow que describe nuestra ventana de presentación.
  • SetPosition. Nuestra aplicación llama a este método para establecer las coordenadas (x, y y y z) de nuestros controles en el espacio de la escena.
  • SetOrientation. Nuestra aplicación llama a este método para establecer el tono y elaw de la cámara.
  • get_Position. Nuestra aplicación accede a esta propiedad para obtener la posición actual de la cámara en el espacio de la escena. Esta propiedad se usa como método para comunicar la posición actual de la cámara a la aplicación.
  • get_LookPoint. Nuestra aplicación accede a esta propiedad para obtener el punto actual hacia el que está orientada la cámara del controlador.
  • Actualización. Lee el estado de los controladores de movimiento y búsqueda y actualiza la posición de la cámara. Llamas continuamente a este método desde el bucle principal de la aplicación para actualizar los datos del controlador de cámara y la posición de la cámara en el espacio de la escena.

Ahora, tiene aquí todos los componentes que necesita para implementar los controles de movimiento y apariencia. Por lo tanto, vamos a conectar estas piezas.

Creación de los eventos de entrada básicos

El distribuidor de eventos de Windows Runtime proporciona 5 eventos que queremos que las instancias de la clase MoveLookController controlen:

Estos eventos se implementan en el tipo CoreWindow. Se supone que tiene un objeto CoreWindow con el que trabajar. Si no sabes cómo obtener una, consulta Cómo configurar tu aplicación de C++ de Plataforma universal de Windows (UWP) para mostrar una vista directX.

A medida que se activan estos eventos mientras se ejecuta la aplicación, los controladores actualizan la información de estado de los controladores definida en nuestros campos privados.

En primer lugar, vamos a rellenar los controladores de eventos del puntero táctil y del mouse. En el primer controlador de eventos, OnPointerPressed(), obtenemos las coordenadas x-y del puntero de CoreWindow que administra nuestra pantalla cuando el usuario hace clic en el mouse o toca la pantalla en la región del controlador de aspecto.

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

Este controlador de eventos comprueba si el puntero no es el mouse (para los fines de este ejemplo, que admite el mouse y la entrada táctil) y si está en el área del controlador de movimiento. Si ambos criterios son verdaderos, comprueba si el puntero se acaba de presionar, en concreto, si este clic no está relacionado con una entrada de movimiento o búsqueda anterior, probando si m_moveInUse es false. Si es así, el controlador captura el punto en el área del controlador de movimiento donde se produjo la presión y establece m_moveInUse en true, de modo que cuando se vuelva a llamar a este controlador, no sobrescribirá la posición inicial de la interacción de entrada del controlador de movimiento. También actualiza el identificador del puntero del controlador de movimiento al identificador del puntero actual.

Si el puntero es el mouse o si el puntero táctil no está en el área del controlador de movimiento, debe estar en el área del controlador de aspecto. Establece m_lookLastPoint a la posición actual en la que el usuario presionaba el botón del mouse o presionaba y presionaba, restablece el delta y actualiza el identificador de puntero del controlador de búsqueda al identificador de puntero actual. También establece el estado del controlador de búsqueda en activo.

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

El controlador de eventos OnPointerMoved se activa cada vez que se mueve el puntero (en este caso, si se arrastra un puntero de pantalla táctil o si se mueve el puntero del mouse mientras se presiona el botón izquierdo). Si el identificador del puntero es el mismo que el identificador del puntero del controlador de movimiento, entonces es el puntero de movimiento; De lo contrario, comprobamos si es el controlador de apariencia que es el puntero activo.

Si es el controlador de movimiento, solo actualizamos la posición del puntero. Seguimos actualizando el evento PointerMoved, ya que queremos comparar la posición final con la primera que capturamos con el controlador de eventos OnPointerPressed.

Si es el controlador de aspecto, las cosas son un poco más complicadas. Necesitamos calcular un nuevo punto de vista y centrar la cámara en ella, por lo que calculamos la diferencia entre el último punto de mirada y la posición actual de la pantalla, y luego multiplicamos frente a nuestro factor de escala, que podemos ajustar para hacer que los movimientos de aspecto sean más pequeños o más grandes en relación con la distancia del movimiento de la pantalla. Con ese valor, calculamos el tono y la guiñada.

Por último, necesitamos desactivar los comportamientos del controlador de movimiento o apariencia cuando el jugador deja de mover el mouse o tocar la pantalla. Usamos OnPointerReleased, al que llamamos cuando se desencadena PointerReleased , para establecer m_moveInUse o m_lookInUse en FALSE y desactivar el movimiento panorámico de la cámara y para cero el identificador de puntero.

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

Hasta ahora, manejamos todos los eventos de pantalla táctil. Ahora, vamos a controlar los eventos de entrada de teclas para un controlador de movimiento basado en 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;
}

Siempre que se presione una de estas teclas, este controlador de eventos establece el estado de movimiento direccional correspondiente en 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;
}

Y cuando se libera la clave, este controlador de eventos lo vuelve a establecer en false. Cuando llamamos a Update, comprueba estos estados de movimiento direccional y mueve la cámara en consecuencia. Esto es un poco más sencillo que la implementación táctil.

Inicializar los controles táctiles y el estado del controlador

Vamos a enlazar los eventos ahora e inicializar todos los campos de estado del controlador.

Initialize

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 toma una referencia a la instancia de CoreWindow de la aplicación como parámetro y registra los controladores de eventos desarrollados para los eventos adecuados en ese CoreWindow. Inicializa los identificadores del puntero de movimiento y apariencia, establece el vector de comando para la implementación del controlador de movimiento de pantalla táctil en cero y establece la cámara mirando directamente cuando se inicia la aplicación.

Obtención y configuración de la posición y orientación de la cámara

Vamos a definir algunos métodos para obtener y establecer la posición de la cámara con respecto a la ventanilla.

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

Actualización de la información de estado del controlador

Ahora, realizamos nuestros cálculos que convierten la información de coordenadas de puntero a la que se realiza el seguimiento en m_movePointerPosition en nueva información de coordenadas correspondiente a nuestro sistema de coordenadas mundial. Nuestra aplicación llama a este método cada vez que actualizamos el bucle principal de la aplicación. Por lo tanto, aquí se calcula la nueva información de posición del punto de vista que queremos pasar a la aplicación para actualizar la matriz de vista antes de la proyección en la ventanilla.

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

}

Dado que no queremos movimiento jittery cuando el jugador usa nuestro controlador de movimiento basado en la entrada táctil, establecemos una zona muerta virtual alrededor del puntero con un diámetro de 32 píxeles. También agregamos velocidad, que es el valor de comando más una tasa de ganancia de movimiento. (Puede ajustar este comportamiento a su gusto, para ralentizar o acelerar la velocidad de movimiento en función de la distancia a la que se mueve el puntero en el área del controlador de movimiento).

Cuando calculamos la velocidad, también traducimos las coordenadas recibidas de los controladores de movimiento y búsqueda al movimiento del punto de búsqueda real que se envía al método que calcula nuestra matriz de vista para la escena. En primer lugar, invertimos la coordenada x, porque si hacemos clic en mover o arrastrar hacia la izquierda o derecha con el controlador de mirada, el punto de mirada gira en la dirección opuesta de la escena, ya que una cámara podría oscilar sobre su eje central. A continuación, intercambiamos los ejes y y y z, ya que una tecla hacia arriba/abajo presiona o presiona el movimiento de arrastre táctil (se lee como un comportamiento del eje Y) en el controlador de movimiento debe traducirse en una acción de cámara que mueve el punto de vista hacia o hacia fuera de la pantalla (el eje Z).

La posición final del punto de búsqueda para el jugador es la última posición más la velocidad calculada, y esto es lo que lee el representador cuando llama al método get_Position (lo más probable es que durante la configuración de cada fotograma). Después, restablecemos el comando move a cero.

Actualización de la matriz de vistas con la nueva posición de la cámara

Podemos obtener una coordenada de espacio de escena en la que nuestra cámara se centra y en la que se actualiza cada vez que se indica a la aplicación que lo haga (cada 60 segundos en el bucle principal de la aplicación, por ejemplo). Este pseudocódigo sugiere el comportamiento de llamada que puede 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
                 ); 

Felicidades. ¡Has implementado controles básicos de movimiento y búsqueda para pantallas táctiles y controles táctiles de entrada de teclado/mouse en tu juego!