Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Aprende a agregar controles tradicionales de movimiento de mouse y teclado (también conocidos como controles mouselook) a tu juego DirectX.
También discutimos el soporte para movimiento-mirada en 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 vista definido para el resto de la pantalla, con la cámara centrada en el último punto que el jugador ha tocado en esa área.
Si esto es un concepto de control desconocido para ti, piensa en esto: el teclado (o el cuadro de entrada direccional táctil) controla tus piernas en este espacio 3D y se comporta como si tus piernas solo fueran capaces de avanzar o retroceder, o desplazarse lateralmente a la 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 objetivo en tu vista, usarás el ratón para centrar la vista de la cámara en ese objetivo y, a continuación, presiona la tecla hacia adelante para moverte hacia él, o hacia atrás para alejarte de él. Para rodear el objetivo, mantén la vista de la cámara centrada en el objetivo y muévete hacia la izquierda o la 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 maneja el movimiento en el plano x-z en relación con el punto de mirada de la cámara; y el controlador de mirada, que apunta al punto de vista de la cámara. Nuestro controlador de movimiento se asigna a las teclas WASD del teclado, y el controlador de apariencia se asigna al ratón. 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.
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, se convierte en un controlador visual; solo tiene que tocar o arrastrar la cámara hacia donde desea orientarla.
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 de movimiento y visualización, 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, utilizando coordenadas de escena.
- m_pitch es la inclinación de la cámara, o su rotación arriba-abajo alrededor del eje x del plano de visión, en radianes.
- m_yaw es la guiñada de la cámara o su rotación de izquierda a 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 gestores 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 y evitar que los movimientos diminutos hagan temblar la vista.
- m_movePointerPosition es el punto en la pantalla al que el reproductor ha movido el puntero. Lo usamos para determinar en qué dirección quería moverse el jugador, examinándolo en relación con 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 vista, tanto las implementaciones del ratón como las 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 soltó el botón izquierdo del ratón con el puntero en nuestra pantalla de juego o dejó de tocar la pantalla.
- OnKeyDown. El jugador presionó una tecla.
- OnKeyUp. El jugador soltó 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.
- establecerPosición. Nuestra aplicación llama a este método para establecer las coordenadas (x, y y z) de nuestros controles en el espacio de la escena.
- establecerOrientación. Nuestra aplicación llama a este método para establecer la inclinación y guiñada 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.
- Actualizar. 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 de 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 para C++ de la Plataforma universal de Windows (UWP) para mostrar una vista directX.
A medida que estos eventos se activan mientras nuestra aplicación se ejecuta, los gestores 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 del CoreWindow que administra nuestra pantalla cuando el usuario hace clic en el mouse o toca la pantalla en la región del controlador de apariencia.
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, comprobando si m_moveInUse es falso. Si es así, el controlador captura el punto exacto en el área del controlador de movimiento donde se produjo la presión y establece m_moveInUse a 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 movimientos al identificador del puntero actual.
Si el puntero es el ratón o si el puntero táctil no está en el área del controlador de movimiento, debe estar en el área del controlador de vista. Establece m_lookLastPoint en la posición actual donde el usuario presionó el botón del mouse o tocó y mantuvo presionado, restablece el delta y actualiza el ID del puntero del controlador de mirada al ID del puntero actual. También establece el estado del controlador de apariencia 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; si no, comprobamos si es el controlador de vista que es el puntero activo.
Si es el controlador de movimiento, solo actualizamos la posición del puntero. Seguimos realizando actualizaciones mientras el evento PointerMoved siga ocurriendo, ya que queremos comparar la posición final con la primera que capturamos con el controlador de eventos OnPointerPressed.
Si es el controlador de apariencia, las cosas son un poco más complicadas. Necesitamos calcular un nuevo punto de mira y centrar la cámara en él, por lo que calculamos la diferencia entre el último punto de mira y la posición actual de la pantalla, y luego multiplicamos contra nuestro factor de escala, que podemos ajustar para hacer que los movimientos de mirada sean más pequeños o más grandes en relación con el desplazamiento de la pantalla. Con ese valor, calculamos la inclinación 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 activa PointerReleased, para establecer m_moveInUse o m_lookInUse en FALSE, desactivar el movimiento panorámico de la cámara y poner a 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, hemos manejado 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.
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.
}
Initialize toma como parámetro una referencia a la instancia CoreWindow de la aplicación y registra los controladores de eventos que desarrollamos para los eventos adecuados en ese CoreWindow. Inicializa los identificadores del puntero de movimiento y del apuntador de vista, establece el vector de comando para la implementación del controlador de movimiento en pantalla táctil a cero y hace que la cámara apunte directamente hacia adelante 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 del puntero seguida en m_movePointerPosition en nueva información de coordenadas respetuosa de 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 observación que queremos pasar a la aplicación para actualizar la matriz de vista antes de la proyección en la ventana de visualización.
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 un movimiento tembloroso cuando el jugador usa nuestro controlador de movimiento de control 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 de vista al movimiento del punto de vista 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 z, ya que al presionar una tecla de arriba/abajo o realizar un movimiento de arrastre táctil (interpretado como un comportamiento del eje y) en el controlador de movimiento debe convertirse en una acción de cámara que mueve el punto de vista hacia dentro o 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 visualización para pantallas táctiles y entrada de teclado/ratón en tu juego!