Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Saiba como adicionar controlos tradicionais de movimento e visualização de rato e teclado (também conhecidos como controlos de visão pelo rato) ao seu jogo DirectX.
Também discutimos o suporte de movimento e visualização para dispositivos de toque, com o controlador de movimento configurado como a seção inferior esquerda da tela, funcionando como uma entrada direcional, e o controlador de visualização configurado para o restante da tela, com a câmara a centrar-se no último lugar tocado pelo jogador nessa área.
Se este é um conceito de controle desconhecido para você, pense da seguinte forma: o teclado (ou a caixa de entrada direcional baseada em toque) controla suas pernas neste espaço 3D e se comporta como se suas pernas fossem capazes apenas de se mover para frente ou para trás, ou para a esquerda e para a direita. O rato (ou ponteiro táctil) controla a sua cabeça. Você usa a cabeça para olhar em uma direção -- esquerda ou direita, para cima ou para baixo, ou em algum lugar nesse plano. Se houver um alvo em sua visualização, você usaria o mouse para centralizar a visão da câmera nesse alvo e, em seguida, pressionaria a tecla para frente para se mover em direção a ele ou para trás para se afastar dele. Para circundar o alvo, você manteria a visão da câmera centralizada no alvo e mover-se-ia para a esquerda ou para a direita ao mesmo tempo. Você pode ver como este é 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 o movimento da câmera fixa do plano x-z, e o mouse é usado para controlar a rotação da câmera em torno dos eixos x e y.
Objetivos
- Adicione controles básicos de movimento ao seu jogo em DirectX para mouse e teclado e telas sensíveis ao toque.
- Implemente uma câmera em primeira pessoa usada para navegar em um ambiente 3D.
Uma nota sobre implementações de controle de toque
Para controlo por toque, implementamos dois controladores: o controlador de movimento, que lida com o movimento no plano x-z em relação ao ponto de observação da câmera; e o controlador de visualização, que orienta o ponto de observação da câmera. O nosso controlador de deslocamento corresponde aos botões WASD do teclado e o controlador de visão corresponde ao rato. Mas para controles de toque, precisamos definir uma região da tela que serve como as entradas direcionais, ou os botões WASD virtuais, com o restante da tela servindo como o espaço de entrada para os controles de aparência.
O nosso ecrã tem o seguinte aspeto.
Quando você move o ponteiro de toque (não o mouse!) no canto inferior esquerdo da tela, qualquer movimento para cima fará a câmera avançar. Qualquer movimento para baixo fará com que a câmera se mova para trás. O mesmo vale para o movimento esquerdo e direito dentro do espaço de ponteiro do controlador de movimento. Fora desse espaço, torna-se um controlador de visualização -- você apenas toca ou arrasta a câmera para onde gostaria que ela estivesse voltada.
Configurar a infraestrutura básica de eventos de entrada
Primeiro, devemos criar nossa classe de controle que usamos para manipular eventos de entrada do mouse e do teclado e atualizar a perspetiva da câmera com base nessa entrada. Como estamos implementando controles move-look, chamamos isso 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 move-look 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
O nosso código contém 4 grupos de campos privados. Vamos rever o propósito de cada um.
Primeiro, definimos alguns campos úteis que contêm nossas informações atualizadas sobre a visão da câmera.
- m_position é a posição da câmera (e, portanto, do plano de visão) na cena 3D, usando coordenadas de cena.
- m_pitch é a inclinação da câmara, ou o 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 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 o nosso controlador de movimento baseado em toque. Não há nada de especial necessário para a implementação de teclado para o controlador de movimento. Apenas lemos eventos de teclado com manipuladores específicos.
- m_moveInUse indica se o controlador de movimentação está em uso.
- m_movePointerID é a ID exclusiva do ponteiro de movimento atual. Usamos isto para diferenciar entre o ponteiro de visualização e o ponteiro de movimento quando verificamos o valor do ID do ponteiro.
- m_moveFirstDown é o ponto na tela onde 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 evitar que pequenos movimentos abalem a visão.
- m_movePointerPosition é o ponto na tela para o qual o jogador moveu o ponteiro no momento. Utilizamo-lo para determinar em que direção quer o jogador mover-se, examinando-o em relação a m_moveFirstDown.
- m_moveCommand é o comando computado final para o controlador de movimento: para cima (para frente), para baixo (para trás), para a esquerda ou para a direita.
Agora, definimos os campos que usamos para o nosso controlador de visualização, tanto as implementações de mouse quanto de toque.
- m_lookInUse indica se o controle de aparência está em uso.
- m_lookPointerID é o identificador exclusivo para o apontador de visual atual. Usamos isto para diferenciar entre o ponteiro de visualização e o ponteiro de movimento quando verificamos o valor do ID do ponteiro.
- m_lookLastPoint é o último ponto, em coordenadas de cena, que foi capturado no quadro anterior.
- m_lookLastDelta é a diferença calculada entre o m_position atual e o m_lookLastPoint.
Finalmente, definimos 6 valores booleanos para os 6 graus de movimento, que usamos para indicar o estado atual de cada ação de movimento direcional (ligado ou desligado):
- 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 rato com o ponteiro no nosso ecrã de jogo, ou arrastou o ponteiro táctil no ecrã.
- OnPointerLançado. O jogador soltou 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 soltou uma chave.
E, finalmente, 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.
- SetPosition. Nosso aplicativo chama esse método para definir as coordenadas (x, y e z) de nossos controles no espaço de cena.
- SetOrientation. O nosso aplicativo chama esse método para definir a inclinação e a guinada da câmera.
- obter_Posicao. 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 comunicar a posição atual da câmera para o aplicativo.
- obter_PontoDeOlhar. Nosso aplicativo acessa essa propriedade para obter o ponto atual para o qual a câmera do controlador está voltada.
- Atualização. Lê o estado dos controladores de movimento e visualização e atualiza a posição da câmara. Você chama continuamente esse método a partir do loop principal do aplicativo para atualizar os dados do controlador da 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 move-look. Então, vamos conectar essas peças.
Criar os eventos de entrada básicos
O dispatcher de eventos do Tempo de Execução do Windows fornece 5 eventos que queremos que instâncias da classe MoveLookController manipulem.
Esses eventos são implementados no tipo CoreWindow . Assumimos que você tem um objeto CoreWindow com o qual trabalhar. Se não souber como obter uma, consulte Como configurar a sua aplicação C++ da Plataforma Universal do Windows (UWP) para apresentar uma vista 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 preencher os manipuladores de eventos do mouse e do ponteiro de toque. No primeiro manipulador de eventos, OnPointerPressed(), obtemos as coordenadas x-y do ponteiro a partir do CoreWindow que gerencia a nossa exibição quando o usuário clica com o mouse ou toca na tela na região do controlador visual.
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 manipulador de eventos verifica se o ponteiro não é o mouse (para os fins deste exemplo, que suporta mouse e toque) e se está na área do controlador de movimento. Se ambos os critérios forem verdadeiros, verifica-se se o ponteiro foi pressionado recentemente, especificamente, se esse clique não está relacionado a um movimento anterior ou à entrada de visualização, testando se m_moveInUse é falso. Em caso afirmativo, o manipulador captura o ponto na área do controlador de movimentação onde a pressão aconteceu e define m_moveInUse como true, de modo que, quando esse manipulador for chamado novamente, ele não substituirá a posição inicial da interação de entrada do controlador de movimento. Ele também atualiza o ID do ponteiro do controlador de movimento para o ID do ponteiro atual.
Se o ponteiro for o rato ou se o ponteiro de toque não estiver na área do controlador de movimento, ele deverá estar na área do controlador de visualização. Ele define m_lookLastPoint para a posição atual onde o usuário pressionou o botão do mouse ou tocou e pressionou, redefine o delta e atualiza o ID do ponteiro do controlador de aparência para o 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 (neste caso, se um ponteiro de tela sensível ao toque estiver sendo arrastado ou se o ponteiro do mouse estiver sendo movido enquanto o botão esquerdo é pressionado). Se o ID do ponteiro for o mesmo que o ID do ponteiro do controlador de movimento, então é o ponteiro de movimento; caso contrário, verificamos se é o controlador look que é o ponteiro ativo.
Se for o controlador de movimento, apenas atualizamos a posição do ponteiro. Continuamos a atualizá-lo enquanto o evento PointerMoved continuar a ser disparado, porque 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 aparência e centralizar a câmera nele, então calculamos o delta entre o último ponto de aparência e a posição atual da tela, e então multiplicamos versus nosso fator de escala, que podemos ajustar para tornar os movimentos de aparência menores ou maiores em relação à distância do movimento da tela. Usando aquele valor, calculamos a inclinação e a guinada.
Finalmente, precisamos desativar os comportamentos do controlador de movimento ou visualização quando o jogador para 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 desligar o movimento panorâmico da câmara, e para redefinir para zero o ID do ponteiro.
OnPointerLançado
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 eventos de entrada de teclas para um controlador de movimentação 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;
}
Contanto que uma dessas teclas seja pressionada, esse manipulador de eventos define o estado de movimento 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 de volta como false. Quando chamamos Update, ele verifica esses estados de movimento direcional e move a câmera de acordo. Isso é um pouco mais simples do que a implementação por 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.
}
Obter e definir a posição e a orientação da câmara
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 do nosso sistema de coordenadas mundiais. Nosso aplicativo chama esse método toda vez que atualizamos o loop principal do aplicativo. Então, é aqui que calculamos as novas informações de posição do ponto de foco que queremos passar para o aplicativo para atualizar a matriz de visualização antes da projeção na janela de visualizaçã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 movimentos nervosos quando o jogador usa nosso controle 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 ao seu gosto, para diminuir ou acelerar a taxa de movimento com base na distância que o ponteiro move na área do controlador de movimento.)
Quando calculamos a velocidade, também traduzimos as coordenadas recebidas dos controladores de movimento e de visão para o movimento do ponto de observação efetivo que nós enviamos para o método que calcula a nossa matriz de visão para a cena. Primeiro, invertemos a coordenada x, porque se clicarmos e movermos ou arrastarmos para a esquerda ou para a direita com o controlador de visualização, o ponto de visão gira na direção oposta na cena, como uma câmara pode girar em torno do seu eixo central. Em seguida, trocamos os eixos y e z, porque um movimento de pressionar ou arrastar tecla para cima/baixo (lido como um comportamento do eixo y) no controlador de movimento deve se traduzir em uma ação da câmera que move o ponto de olhar para dentro ou para fora da tela (o eixo z).
A posição final do ponto de observação para o jogador é a última posição acrescentada à velocidade calculada, e isso é o que é lido pelo renderizador quando ele chama o método get_Position (provavelmente durante o processamento de cada fotograma). Depois disso, redefinimos o comando move para zero.
Atualizar a matriz de visualização com a nova posição da câmara
Podemos obter uma coordenada de espaço de cena na qual a nossa câmara está focada e que é atualizada sempre que indicar ao seu aplicativo que o faça (de 60 em 60 segundos no ciclo 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 movimento para telas sensíveis ao toque e controles de toque de entrada de teclado/mouse em seu jogo!