Share via


Controlli Touch per i giochi

Informazioni su come aggiungere controlli touch di base a un gioco UWP (Universal Windows Platform) C++ con DirectX. Illustriamo come aggiungere controlli basati sul tocco per spostare una telecamera a piano fisso in un ambiente Direct3D, dove il trascinamento con un dito o uno stilo sposta la prospettiva della telecamera.

È possibile incorporare questi controlli in giochi in cui si desidera che il giocatore trascini per scorrere o effettuare una panoramica in un ambiente 3D, ad esempio una mappa o un campo di gioco. Ad esempio, in un gioco di strategia o puzzle, è possibile utilizzare tali controlli per consentire al giocatore di visualizzare un ambiente di gioco più grande rispetto allo schermo eseguendo una panoramica verso sinistra o verso destra.

Nota Il nostro codice funziona anche con i controlli di panoramica basati su mouse. Gli eventi correlati al puntatore vengono astratti dalle API di Windows Runtime, in modo che possano gestire eventi puntatore basati sul tocco o sul mouse.

 

Obiettivi

  • Creare un controllo semplice di trascinamento touch per la panoramica di una telecamera a piano fisso in un gioco DirectX.

Configurare l'infrastruttura di eventi touch di base

Prima di tutto, definiamo il nostro tipo di controller di base, il CameraPanController, in questo caso. Qui, definiamo un controller come idea astratta, la serie di comportamenti che l'utente può eseguire.

La classe CameraPanController è una raccolta regolarmente aggiornata di informazioni sullo stato del controller della telecamera e consente all'app di ottenere tali informazioni dal suo ciclo di aggiornamento.

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
{
}

A questo punto, creiamo un'intestazione che definisce lo stato del controller della telecamera e i metodi di base e i gestori di eventi che implementano le interazioni del controller della telecamera.

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

I campi privati contengono lo stato corrente del controller della telecamera. Riesaminiamoli.

  • m_position è la posizione della telecamera nello spazio di scena. In questo esempio il valore della coordinata z è fissato a 0. Potremmo utilizzare un DirectX::XMFLOAT2 per rappresentare questo valore, ma ai fini di questo esempio e dell'estendibilità futura, utilizziamo un DirectX::XMFLOAT3. Passiamo questo valore attraverso la proprietà get_Position all'app stessa in modo che possa aggiornare la viewport di conseguenza.
  • m_panInUse è un valore booleano che indica se un'operazione di panoramica è attiva o, in particolare, se il giocatore sta toccando lo schermo e spostando la telecamera.
  • m_panPointerID è un ID univoco per il puntatore. Non utilizzeremo questo nell'esempio, ma è consigliabile associare la classe di stato del controller a un puntatore specifico.
  • m_panFirstDown è il punto sullo schermo dove il giocatore ha toccato per la prima volta lo schermo o ha fatto clic sul mouse durante l'azione panoramica della telecamera. Utilizzeremo questo valore in un secondo momento per impostare una zona morta in modo da evitare eventuali instabilità quando lo schermo viene toccato o se il mouse viene agitato leggermente.
  • m_panPointerPosition è il punto sullo schermo sul quale il giocatore ha spostato correntemente il puntatore. Lo utilizziamo per determinare la direzione in cui il giocatore voleva spostarsi esaminandolo rispetto a m_panFirstDown.
  • m_panCommand è il comando calcolato finale per il controller della telecamera: su, giù, sinistra o destra. Poiché stiamo lavorando con una telecamera fissa rispetto al piano x-y, questo potrebbe essere invece un valore DirectX::XMFLOAT2

Utilizziamo questi 3 gestori di eventi per aggiornare le informazioni sullo stato del controller della telecamera.

  • OnPointerPressed è un gestore di eventi che la nostra app chiama quando il giocatore preme un dito sulla superficie touch e il puntatore viene spostato nelle coordinate della pressione.
  • OnPointerMoved è un gestore di eventi che la nostra app chiama quando il giocatore scorre un dito sulla superficie touch. Viene aggiornato con le nuove coordinate del percorso di trascinamento.
  • OnPointerReleased è un gestore di eventi che la nostra app chiama quando il giocatore rimuove il dito premente dalla superficie touch.

Infine, utilizziamo questi metodi e proprietà per inizializzare, accedere e aggiornare le informazioni sullo stato del controller della telecamera.

  • Initialize è un gestore di eventi che la nostra app chiama per inizializzare i controlli e collegarli all'oggetto CoreWindow che descrive la finestra di visualizzazione.
  • SetPosition è un metodo che la nostra app chiama per impostare le coordinate (x, y e z) dei controlli nello spazio di scena. Notare che la nostra coordinata z è 0 in tutta questa esercitazione.
  • get_Position è una proprietà a cui la nostra app accede per ottenere la posizione corrente della telecamera nello spazio di scena. Questa proprietà viene utilizzata come metodo per comunicare la posizione corrente della telecamera all'app.
  • get_FixedLookPoint è una proprietà a cui la nostra app accede per ottenere il punto corrente verso cui è orientata la telecamera del controller. In questo esempio, è bloccato normale rispetto al piano x-y.
  • Update è un metodo che legge lo stato del controller e aggiorna la posizione della telecamera. Si chiama continuamente questo <qualcosa> dal ciclo principale dell'app per aggiornare i dati del controller della telecamera e la posizione della telecamera nello spazio di scena.

A questo punto, sono disponibili tutti i componenti necessari per implementare i controlli touch. È possibile rilevare quando e dove si sono verificati gli eventi del puntatore del mouse o touch e quale sia l'azione. È possibile impostare la posizione e l'orientamento della telecamera rispetto allo spazio di scena e tenere traccia delle modifiche. Infine, è possibile comunicare la nuova posizione della telecamera all'app chiamante.

Colleghiamo ora questi pezzi insieme.

Creare gli eventi touch di base

Il dispatcher di eventi di Windows Runtime fornisce 3 eventi che desideriamo che la nostra app gestisca:

Questi eventi vengono implementati nel tipo CoreWindow. Supponiamo di avere un oggetto CoreWindow con cui lavorare. Per maggiori informazioni, vedere Come configurare un'app UWP C++ per visualizzare una vista DirectX.

Man mano che questi eventi vengono generati durante l'esecuzione dell'app, i gestori aggiornano le informazioni sullo stato del controller della telecamera definite nei nostri campi privati.

Prima di tutto, popoliamo i gestori di eventi del puntatore touch. Nel primo gestore di eventi, OnPointerPressed, otteniamo le coordinate x-y del puntatore da CoreWindow che gestisce la nostra visualizzazione quando l'utente o tocca lo schermo o fa clic sul 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;
    }
    
}

Utilizziamo questo gestore per informare l'istanza CameraPanController corrente che il controller della telecamera deve essere considerato come attivo impostando m_panInUse su TRUE. In questo modo, quando l'app chiama Update, utilizzerà i dati della posizione corrente per aggiornare la viewport.

Ora che abbiamo stabilito i valori di base per il movimento della telecamera quando l'utente tocca lo schermo o fa clic nella finestra di visualizzazione, dobbiamo determinare cosa fare quando l'utente trascina la pressione sullo schermo o sposta il mouse con il pulsante premuto.

Il gestore di eventi OnPointerMoved viene attivato ogni volta che il puntatore si sposta, a ogni piccolo movimento di trascinamento del giocatore sullo schermo. Dobbiamo mantenere l'app consapevole della posizione corrente del puntatore e questo è il modo in cui lo facciamo.

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

Infine, dobbiamo disattivare il comportamento di panoramica della telecamera quando il giocatore smette di toccare lo schermo. Utilizziamo OnPointerReleased, che viene chiamato quando viene generato PointerReleased, per impostare m_panInUse su FALSE e interrompere il movimento panoramico della telecamera, e per impostare l'ID puntatore su 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;
}

Inizializzare i controlli touch e lo stato del controller

Associamo gli eventi e inizializziamo tutti i campi di stato di base del controller della telecamera.

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 accetta un riferimento all'istanza CoreWindow dell'app come parametro e registra i gestori di eventi sviluppati negli eventi appropriati in tale CoreWindow.

Recupero e impostazione della posizione del controller della telecamera

Definiamo alcuni metodi per ottenere e impostare la posizione del controller della telecamera nello spazio di scena.

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 è un metodo pubblico che possiamo chiamare dalla nostra app se dobbiamo impostare la posizione del controller della telecamera su un punto specifico.

get_Position è la nostra proprietà pubblica più importante: è il modo in cui l'app ottiene la posizione corrente del controller della telecamera nello spazio di scena in modo che possa aggiornare la viewport di conseguenza.

get_FixedLookPoint è una proprietà pubblica che, in questo esempio, ottiene un punto di visione normale rispetto al piano x-y. È possibile modificare questo metodo per utilizzare le funzioni trigonometriche, seno e coseno, quando si calcolano i valori delle coordinate x, y e z se si desidera creare più angoli obliqui per la telecamera fissa.

Aggiornamento delle informazioni di stato del controller della telecamera

A questo punto, eseguiamo i nostri calcoli che convertono le informazioni sulle coordinate del puntatore rilevate in m_panPointerPosition in nuove informazioni sulle coordinate rispettive del nostro spazio di scena 3D. La nostra app chiama questo metodo ogni volta che aggiorniamo il ciclo principale dell'app. È qui che calcoliamo le nuove informazioni sulla posizione che vogliamo trasferire all'app che vengono utilizzate per aggiornare la matrice di visualizzazione prima della proiezione nella viewport.


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

}

Poiché non desideriamo un'instabilità del touch o del mouse che potrebbe rendere poco fluida la nostra panoramica della telecamera, impostiamo una zona morta intorno al puntatore con un diametro di 32 pixel. Abbiamo anche un valore di velocità, che in questo caso è 1:1 con il passaggio del pixel del puntatore oltre la zona morta. È possibile regolare questo comportamento in modo da rallentare o velocizzare la velocità di movimento.

Aggiornamento della matrice di visualizzazione con la nuova posizione della telecamera

Possiamo ora ottenere una coordinata dello spazio di scena su cui si concentra la nostra telecamera e che viene aggiornata ogni volta che si indica all'app di farlo (ad esempio ogni 60 secondi nel ciclo principale dell'app). Questo pseudocodice suggerisce il comportamento di chiamata che è possibile implementare:

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

Complimenti! Hai implementato un semplice set di controlli touch di panoramica della telecamera in un gioco.