Partilhar via


Controles de toque para jogos

Saiba como adicionar controlos táteis 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, onde arrastar com um dedo ou caneta muda a perspetiva da câmera.

Você pode incorporar esses controles em jogos onde você deseja que o jogador arraste para rolar ou mover 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 do que a tela, movendo para a esquerda ou para a direita.

Nota 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 manipular eventos de ponteiro baseados em toque ou mouse.

 

Objetivos

  • Crie um controle de arrasto por toque simples para mover uma câmera de plano fixo em um jogo DirectX.

Configurar a infraestrutura 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 que é regularmente atualizada com informações sobre o estado do controlador da câmara e fornece uma forma para a nossa aplicação obter essas informações a partir do 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 da câmera e os métodos básicos e manipuladores de eventos que implementam as interações do controlador da 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 analisá-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 os fins deste exemplo e extensibilidade futura, usamos um DirectX::XMFLOAT3. Passamos este valor através da propriedade get_Position para a própria aplicação, para que ela possa atualizar a janela de visualização de acordo.
  • m_panInUse é um valor booleano que indica se uma operação de panorâmica está ativa; ou, mais especificamente, se o leitor está a tocar no ecrã e a mover a câmara.
  • m_panPointerID é um ID exclusivo para o ponteiro. Não usaremos isso no exemplo, mas é uma boa prática associar sua classe de estado do controlador a um ponteiro específico.
  • m_panFirstDown é o ponto na tela onde 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 mais tarde para definir uma zona morta para evitar desvios quando a tela é tocada ou se o mouse tremer um pouco.
  • m_panPointerPosition é o ponto na tela onde o jogador moveu o ponteiro no momento. Nós usamos isto para determinar em que direção o jogador queria mover-se, analisando-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 a trabalhar com uma câmera fixa ao plano x-y, este pode ser um valor DirectX::XMFLOAT2 em vez disso.

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

  • OnPointerPressed é um manipulador de eventos que a nossa aplicação chama quando o jogador pressiona um dedo na superfície de toque e o ponteiro é movido para as coordenadas da pressão.
  • OnPointerMoved é um manipulador de eventos que nosso aplicativo chama quando o jogador desliza um dedo pela superfície de toque. Atualiza-se com as novas coordenadas da trajetória de arrasto.
  • OnPointerReleased é um manipulador de eventos que a nossa aplicação ativa quando o jogador remove o dedo pressionado da superfície de toque.

Finalmente, 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 a nossa aplicação utiliza para definir as coordenadas (x, y e z) dos seus controlos 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 para o 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 é bloqueado perpendicularmente ao plano x-y.
  • Update é um método que lê o estado do controlador e atualiza a posição da câmera. Você continuamente chama este <alguma coisa> do loop principal para atualizar os dados do controlador da câmara e a posição da câmara no espaço da cena.

Agora, você tem aqui todos os componentes que você precisa para implementar controles de toque. Você pode detetar quando e onde ocorreram os eventos de ponteiro de toque ou de rato, 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 acompanhar as alterações. Finalmente, você pode comunicar a nova posição da câmera para o 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 . Assumimos que você tem um objeto CoreWindow com o qual trabalhar. Para saber mais, veja 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 da câmera definidas em nossos campos privados.

Primeiro, vamos configurar 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 este manipulador para permitir que a instância atual do CameraPanController saiba que o controlador de câmera deve ser tratada como ativa, definindo m_panInUse como VERDADEIRO. Dessa forma, quando o aplicativo chama Update, ele usará os dados de posição atuais para atualizar a janela de visualização.

Agora que estabelecemos os valores básicos para o movimento da câmara quando o utilizador toca na tela ou clica com o botão na janela de exibição, deveremos determinar o que fazer quando o utilizador arrasta mantendo a pressão na tela ou move o rato mantendo 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 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 pan da câmera quando o jogador para de tocar na tela. Usamos OnPointerReleased, que é chamado quando PointerReleased é acionado, para definir m_panInUse como FALSE e desligar o movimento de deslocação da câmara, além de definir o ID do ponteiro como 0.

OnPointerLançado

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.

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

}

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

Obter e definir a posição do controlador da câmara

Vamos definir alguns métodos para obter e definir a posição do controlador da câmera no espaço de 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 a partir do nosso aplicativo se precisarmos definir a posição do controlador da câmera para um ponto específico.

get_Position é a nossa propriedade pública mais importante: é a maneira como a nossa aplicação obtém a posição atual do controlador da câmera no espaço de cena para que 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 que é normal ao plano x-y. Você pode alterar esse método para usar as funções trigonométricas, sin e cos, ao calcular os valores das coordenadas x, y e z se quiser criar mais ângulos oblíquos para a câmera fixa.

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

Agora, realizamos os nossos cálculos que convertem as informações das coordenadas do ponteiro rastreadas em m_panPointerPosition em novas informações de coordenadas relativas ao nosso espaço de cenário 3D. Nosso aplicativo chama esse método toda vez 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 visualização antes da projeção no 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 o desvio do mouse tornem o movimento panorâmico da nossa câmera irregular, definimos uma zona morta com um diâmetro de 32 pixels ao redor do ponteiro. Também temos um valor de velocidade, que neste caso é de 1:1 com o deslocamento do ponteiro em pixels além da zona morta. Você pode ajustar esse comportamento para diminuir ou acelerar a taxa de movimento.

Atualizar a matriz de visualização com a nova posição da câmara

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). 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 de movimento panorâmico da câmera em seu jogo.