Compartilhar via


Controles de toque para jogos

Saiba como adicionar controles de toque básicos ao seu jogo C++ da Plataforma Universal do Windows (UWP) com DirectX. Mostramos como adicionar controles baseados em toque para mover uma câmera de plano fixo em um ambiente Direct3D, em que arrastar com um dedo ou caneta muda a perspectiva da câmera.

Você pode incorporar esses controles em jogos em que deseja que o jogador arraste para rolar ou deslocar sobre um ambiente 3D, como um mapa ou 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 visualize um ambiente de jogo maior que a tela movendo para a esquerda ou para a direita.

Observação Nosso código também funciona com controles de movimento panorâmico baseados em mouse. Os eventos relacionados ao ponteiro são abstraídos pelas APIs do Tempo de Execução do Windows, para que possam lidar com eventos de ponteiro baseados em toque ou mouse.

 

Objetivos

  • Crie um controle de arrastar por toque simples para fazer o movimento panorâmico de uma câmera de plano fixo em um jogo DirectX.

Configurar a infra-estrutura básica de eventos de toque

Primeiro, definimos nosso tipo básico de controlador, o CameraPanController, neste 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 atualizada regularmente de informações sobre o estado do controlador da 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 da 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 é fixado 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 por meio da propriedade get_Position para o próprio aplicativo para que ele possa atualizar o visor de acordo.
  • 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 em que o jogador tocou pela primeira vez na tela ou clicou com o mouse durante a ação de panorâmica da câmera. Usamos esse valor posteriormente para definir uma zona morta para evitar jitter quando a tela é tocada ou se o mouse treme um pouco.
  • m_panPointerPosition é o ponto na tela onde o jogador moveu o ponteiro no momento. Nós o usamos para determinar em que direção o jogador queria se mover, examinando-o em relação a m_panFirstDown.
  • m_panCommand é o comando calculado 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 fixada no plano x-y, esse pode ser um valor DirectX::XMFLOAT2.

Usamos esses 3 manipuladores de eventos para atualizar as informações de estado do controlador da câmera.

  • OnPointerPressed é um manipulador de eventos que nosso aplicativo chama quando os jogadores pressionam um dedo na superfície de toque e o ponteiro é movido para as coordenadas do pressionamento.
  • OnPointerMoved é um manipulador de eventos que nosso aplicativo chama quando o jogador passa o dedo pela superfície de toque. Ele é atualizado com as novas coordenadas do caminho de arrastar.
  • OnPointerReleased é um manipulador de eventos que nosso aplicativo chama quando o player remove o dedo pressionado da superfície de toque.

Por fim, usamos esses métodos e propriedades para inicializar, acessar e atualizar as informações de estado do controlador da câmera.

  • Initialize é um manipulador de eventos que nosso aplicativo chama para inicializar os controles e anexá-los ao objeto CoreWindow que descreve sua janela de exibição.
  • SetPosition é um método que nosso aplicativo 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 normalmente para o 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 de algo> 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 controles de toque. Você pode detectar quando e onde os eventos de toque ou ponteiro do mouse ocorreram e qual é a ação. Você pode definir a posição e a orientação da câmera em relação ao espaço da cena e rastrear 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.

Criar os eventos de toque básicos

O dispatcher de eventos do Tempo de Execução do Windows fornece 3 eventos que queremos que nosso aplicativo manipule:

Esses eventos são implementados no tipo CoreWindow . Supomos que você tenha um objeto CoreWindow para trabalhar. Para obter mais informações, consulte Como configurar seu aplicativo UWP C++ para exibir um modo de exibição 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 preencher os manipuladores de eventos do ponteiro de toque. No primeiro manipulador de eventos, OnPointerPressed, obtemos as coordenadas x-y do ponteiro do CoreWindow que gerencia nossa exibição quando o usuário toca na tela ou clica no 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 atual do CameraPanController saiba que o controlador da câmera deve ser tratado como ativo definindo m_panInUse como TRUE. Dessa forma, quando o aplicativo chamar Update , ele usará os dados de posição atual para atualizar a janela de exibição.

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 a tela pressionada ou move o mouse com o botão pressionado.

O manipulador de eventos OnPointerMoved é acionado sempre que o ponteiro se move, a cada tique que o jogador o arrasta na tela. Precisamos manter o aplicativo ciente da localização atual do ponteiro, e é assim que 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;
}

Finalmente, precisamos desativar o comportamento da panorâmica da câmera quando o jogador para de tocar na tela. Usamos OnPointerReleased, que é chamado quando PointerReleased é disparado, para definir m_panInUse como FALSE e desativar o movimento panorâmico da câmera e 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ásico do controlador da câmera.

Initialize

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

}

Initialize usa uma referência à instância CoreWindow do aplicativo como um parâmetro e registra os manipuladores de eventos que desenvolvemos para os eventos apropriados nesse CoreWindow.

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

Vamos definir alguns métodos para obter e definir a posição do controlador da 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 da 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 da câmera no espaço da cena para que ele possa atualizar a janela de visualização de acordo.

get_FixedLookPoint é uma propriedade pública que, neste exemplo, obtém um ponto de visão normal ao plano x-y. Você pode alterar esse método para usar as funções trigonométricas, sen e cos, ao calcular os valores das coordenadas x, y e z se quiser criar ângulos mais oblíquos para a câmera fixa.

Atualizando as informações de estado do controlador da câmera

Agora, realizamos nossos cálculos que convertem as informações de coordenadas 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 toda vez que atualizamos o loop do aplicativo principal. 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 na janela de visualização.


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 câmera irregular Também temos um valor de velocidade, que neste caso é 1:1 com a travessia de pixels do ponteiro além da zona morta. Você pode ajustar esse comportamento para desacelerar 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 seu aplicativo para fazer isso (a cada 60 segundos no loop principal do aplicativo, por exemplo). Esse 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 de panorâmica da câmera em seu jogo.