Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Saiba como adicionar controles de toque básicos ao seu jogo C++ da Plataforma Universal do Windows (UWP) com o DirectX. Mostramos como adicionar controles baseados em toque para mover uma câmera de plano fixo em um ambiente Direct3D, onde arrastar com um dedo ou caneta desloca a perspectiva da câmera.
Você pode incorporar esses controles em jogos em que deseja que o jogador arraste para rolar ou percorrer um ambiente 3D, como um mapa ou um campo de jogo. Por exemplo, em um jogo de estratégia ou quebra-cabeça, você pode usar esses controles para permitir que o jogador exiba um ambiente de jogo maior que a tela, fazendo movimento panorâmico para a esquerda ou para a direita.
Observação Nosso código também funciona com controles de rolagem baseados em mouse. Os eventos relacionados ao ponteiro são abstraídos pelas APIs do Windows Runtime, para que possam lidar com eventos de ponteiro baseados em toque ou mouse.
Objectivos
- Crie um controle de arrastar por toque simples para panoramizar uma câmera fixa em um jogo do DirectX.
Configurar a infraestrutura básica para eventos de toque
Primeiro, definimos nosso tipo de controlador básico, o CameraPanController, nesse caso. Aqui, definimos um controlador como uma ideia abstrata, o conjunto de comportamentos que o usuário pode executar.
A classe CameraPanController é uma coleção de informações atualizada regularmente sobre o estado do controlador de câmera e fornece uma maneira de nosso aplicativo obter essas informações de seu loop de atualização.
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 CameraPanController
{
}
Agora, vamos criar um cabeçalho que define o estado do controlador de câmera e os métodos básicos e manipuladores de eventos que implementam as interações do controlador de câmera.
ref class CameraPanController
{
private:
// Properties of the controller object
DirectX::XMFLOAT3 m_position; // the position of the camera
// Properties of the camera pan control
bool m_panInUse;
uint32 m_panPointerID;
DirectX::XMFLOAT2 m_panFirstDown;
DirectX::XMFLOAT2 m_panPointerPosition;
DirectX::XMFLOAT3 m_panCommand;
internal:
// Accessor to set the position of the controller
void SetPosition( _In_ DirectX::XMFLOAT3 pos );
// Accessor to set the fixed "look point" of the controller
DirectX::XMFLOAT3 get_FixedLookPoint();
// Returns the position of the controller object
DirectX::XMFLOAT3 get_Position();
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
);
// Set up the Controls supported by this controller
void Initialize( _In_ Windows::UI::Core::CoreWindow^ window );
void Update( Windows::UI::Core::CoreWindow ^window );
}; // Class CameraPanController
Os campos privados contêm o estado atual do controlador de câmera. Vamos revisá-los.
- m_position é a posição da câmera no espaço da cena. Neste exemplo, o valor da coordenada z é fixo em 0. Poderíamos usar um DirectX::XMFLOAT2 para representar esse valor, mas para fins deste exemplo e extensibilidade futura, usamos um DirectX::XMFLOAT3. Passamos esse valor pela propriedade get_Position para o próprio aplicativo para que ele possa atualizar a janela de visualização adequadamente.
- m_panInUse é um valor booleano que indica se uma operação de panorâmica está ativa; ou, mais especificamente, se o jogador está tocando na tela e movendo a câmera.
- m_panPointerID é uma ID exclusiva para o ponteiro. Não usaremos isso no exemplo, mas é uma boa prática associar a classe de estado do controlador a um ponteiro específico.
- m_panFirstDown é o ponto na tela onde o jogador tocou ou clicou com o mouse pela primeira vez durante a ação de panorâmica da câmera. Usamos esse valor mais tarde para definir uma zona morta para evitar tremulação quando a tela é tocada ou se o mouse treme um pouco.
- m_panPointerPosition é o ponto na tela em que o jogador moveu o ponteiro no momento. Usamos-o para determinar a direção que o jogador queria mover examinando-o em relação a m_panFirstDown.
- m_panCommand é o comando computado final para o controlador da câmera: para cima, para baixo, para a esquerda ou para a direita. Como estamos trabalhando com uma câmera fixa no plano x-y, esse pode ser um valor DirectX::XMFLOAT2 em vez disso.
Usamos esses três manipuladores de eventos para atualizar as informações de estado do controlador de câmera.
- OnPointerPressed é um tratador de evento que nosso aplicativo chama quando o jogador pressiona um dedo na superfície de toque e o ponteiro é movido para as coordenadas do toque.
- OnPointerMoved é um manipulador de eventos que nosso aplicativo chama quando o jogador passa um dedo pela superfície de toque. Ele é atualizado com as novas coordenadas da trajetória de arrasto.
- OnPointerReleased é um manipulador de eventos que nosso aplicativo chama quando o jogador remove o dedo que estava pressionando a superfície de toque.
Por fim, usamos esses métodos e propriedades para inicializar, acessar e atualizar as informações de estado do controlador de câmera.
- Inicializar é um manipulador de eventos que nosso aplicativo chama para iniciar os controles e anexá-los ao objeto CoreWindow que descreve sua janela de exibição.
- SetPosition é um método que nossa aplicação chama para definir as coordenadas (x, y e z) de seus controles no espaço da cena. Observe que nossa coordenada z é 0 ao longo deste tutorial.
- get_Position é uma propriedade que nosso aplicativo acessa para obter a posição atual da câmera no espaço da cena. Você usa essa propriedade como a maneira de comunicar a posição atual da câmera ao aplicativo.
- get_FixedLookPoint é uma propriedade que nosso aplicativo acessa para obter o ponto atual para o qual a câmera do controlador está voltada. Neste exemplo, ele está bloqueado perpendicular ao plano x-y.
- Update é um método que lê o estado do controlador e atualiza a posição da câmera. Você chama isso continuamente <algo> 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 controles de toque. Você pode detectar quando e onde os eventos de toque ou ponteiro do mouse ocorreram e qual ação foi realizada. Você pode definir a posição e a orientação da câmera em relação ao espaço da cena e acompanhar as alterações. Por fim, você pode comunicar a nova posição da câmera ao aplicativo de chamada.
Agora, vamos conectar essas peças.
Crie os eventos básicos de toque
O dispatcher de eventos do Windows Runtime fornece 3 eventos que queremos que nosso aplicativo manipule:
Esses eventos são implementados no tipo CoreWindow. Presumimos que você tenha um objeto CoreWindow com o qual trabalhar. Para obter mais informações, consulte Como configurar seu aplicativo UWP C++ 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 do controlador de câmera definidas em nossos campos privados.
Primeiro, vamos configurar os manipuladores de eventos de ponteiro tátil. No manipulador do primeiro evento, OnPointerPressed, obtemos as coordenadas x-y do ponteiro a partir do CoreWindow que gerencia nossa exibição, quando o usuário toca a tela ou clica com o mouse.
OnPointerPressed
void CameraPanController::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 ( !m_panInUse ) // If no pointer is in this control yet.
{
m_panFirstDown = position; // Save the location of the initial contact.
m_panPointerPosition = position;
m_panPointerID = pointerID; // Store the id of the pointer using this control.
m_panInUse = TRUE;
}
}
Usamos esse manipulador para permitir que a instância CameraPanController
Agora que estabelecemos os valores base para o movimento da câmera quando o usuário toca na tela ou clica na janela de exibição, devemos determinar o que fazer quando o usuário arrasta o dedo na tela ou move o mouse com o botão pressionado.
O manipulador de eventos OnPointerMoved é acionado sempre que o ponteiro se move, a cada instante em que o jogador o arrasta na tela. Precisamos manter o aplicativo ciente da localização atual do ponteiro e é assim que o fazemos.
OnPointerMoved
void CameraPanController::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 );
m_panPointerPosition = position;
}
Por fim, precisamos desativar o movimento de panorâmica da câmera quando o jogador parar de tocar na tela. Usamos OnPointerReleased, que é chamado quando PointerReleased é acionado, para definir m_panInUse como FALSE e desativar o movimento de panorâmica da câmera, além de definir a ID do ponteiro como 0.
OnPointerReleased
void CameraPanController::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 );
m_panInUse = FALSE;
m_panPointerID = 0;
}
Inicializar os controles de toque e o estado do controlador
Vamos conectar os eventos e inicializar todos os campos de estado básicos do controlador de câmera.
inicializar
void CameraPanController::Initialize( _In_ CoreWindow^ window )
{
// Start receiving touch/mouse events.
window->PointerPressed +=
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerPressed);
window->PointerMoved +=
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerMoved);
window->PointerReleased +=
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &CameraPanController::OnPointerReleased);
// Initialize the state of the controller.
m_panInUse = FALSE;
m_panPointerID = 0;
// Initialize this as it is reset on every frame.
m_panCommand = DirectX::XMFLOAT3( 0.0f, 0.0f, 0.0f );
}
Obtendo e definindo a posição do controlador de câmera
Vamos definir alguns métodos para obter e definir a posição do controlador de câmera no espaço da cena.
void CameraPanController::SetPosition( _In_ DirectX::XMFLOAT3 pos )
{
m_position = pos;
}
// Returns the position of the controller object
DirectX::XMFLOAT3 CameraPanController::get_Position()
{
return m_position;
}
DirectX::XMFLOAT3 CameraPanController::get_FixedLookPoint()
{
// For this sample, we don't need to use the trig functions because our
// look point is fixed.
DirectX::XMFLOAT3 result= m_position;
result.z += 1.0f;
return result;
}
SetPosition é um método público que podemos chamar de nosso aplicativo se precisarmos definir a posição do controlador de câmera para um ponto específico.
get_Position é nossa propriedade pública mais importante: é a maneira como nosso aplicativo obtém a posição atual do controlador de câmera no espaço de cena para que ele possa atualizar o visor adequadamente.
get_FixedLookPoint é uma propriedade pública que, neste exemplo, obtém um ponto de visão perpendicular ao plano x-y. Você pode alterar esse método para usar as funções trigonométricas, seno e cos, ao calcular as coordenadas x, y e z caso queira criar ângulos mais oblíquos para a câmera fixa.
Atualizando as informações de estado do controlador de câmera
Agora, executamos nossos cálculos que convertem as informações de coordenada do ponteiro rastreadas em m_panPointerPosition em novas informações de coordenadas respectivas do nosso espaço de cena 3D. Nosso aplicativo chama esse método sempre que atualizamos o loop principal do aplicativo. Nele, calculamos as novas informações de posição que queremos passar para o aplicativo que é usado para atualizar a matriz de exibição antes da projeção para o visor.
void CameraPanController::Update( CoreWindow ^window )
{
if ( m_panInUse )
{
pointerDelta.x = m_panPointerPosition.x - m_panFirstDown.x;
pointerDelta.y = m_panPointerPosition.y - m_panFirstDown.y;
if ( pointerDelta.x > 16.0f ) // Leave 32 pixel-wide dead spot for being still.
m_panCommand.x += 1.0f;
else
if ( pointerDelta.x < -16.0f )
m_panCommand.x += -1.0f;
if ( pointerDelta.y > 16.0f )
m_panCommand.y += 1.0f;
else
if (pointerDelta.y < -16.0f )
m_panCommand.y += -1.0f;
}
DirectX::XMFLOAT3 command = m_panCommand;
// Our velocity is based on the command.
DirectX::XMFLOAT3 Velocity;
Velocity.x = command.x;
Velocity.y = command.y;
Velocity.z = 0.0f;
// Integrate
m_position.x = m_position.x + Velocity.x;
m_position.y = m_position.y + Velocity.y;
m_position.z = m_position.z + Velocity.z;
// Clear the movement input accumulator for use during the next frame.
m_panCommand = DirectX::XMFLOAT3( 0.0f, 0.0f, 0.0f );
}
Como não queremos que o toque ou a tremulação do mouse tornem a movimentação da câmera brusca, definimos uma zona morta ao redor do ponteiro com um diâmetro de 32 pixels. Também temos um valor de velocidade, que nesse caso é proporcional de 1:1 ao movimento do pixel do cursor após a zona morta. Você pode ajustar esse comportamento para reduzir ou acelerar a taxa de movimento.
Atualizando a matriz de exibição com a nova posição da câmera
Agora 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:
myCameraPanController->Update( m_window );
// Update the view matrix based on the camera position.
myCamera->MyMethodToComputeViewMatrix(
myController->get_Position(), // The position in the 3D scene space.
myController->get_FixedLookPoint(), // The point in the space we are looking at.
DirectX::XMFLOAT3( 0, 1, 0 ) // The axis that is "up" in our space.
);
Parabéns! Você implementou um conjunto simples de controles de toque panorâmicos de câmera em seu jogo.