Partager via


Contrôles tactiles pour les jeux

Découvrez comment ajouter des contrôles tactiles de base à votre jeu C++ plateforme Windows universelle (UWP) avec DirectX. Nous vous montrons comment ajouter des contrôles tactiles pour déplacer une caméra à plan fixe dans un environnement Direct3D, où le glissement avec un doigt ou un stylet déplace la perspective de la caméra.

Vous pouvez incorporer ces contrôles dans les jeux dans lesquels vous souhaitez que le joueur effectue un glisser-faire glisser pour faire défiler ou parcourir un environnement 3D, tel qu’une carte ou un champ de jeu. Par exemple, dans un jeu de stratégie ou de puzzle, vous pouvez utiliser ces contrôles pour permettre au joueur d’afficher un environnement de jeu plus grand que l’écran en panoramique gauche ou droite.

Notez que notre code fonctionne également avec les contrôles panoramiques basés sur la souris. Les événements liés au pointeur sont abstraits par les API Windows Runtime, afin qu’ils puissent gérer les événements de pointeur tactiles ou basés sur la souris.

 

Objectifs

  • Créez un contrôle de glisser tactile simple pour le panoramique d’une caméra à plan fixe dans un jeu DirectX.

Configurer l’infrastructure d’événements tactiles de base

Tout d’abord, nous définissons notre type de contrôleur de base, cameraPanController, dans ce cas. Ici, nous définissons un contrôleur comme une idée abstraite, l’ensemble des comportements que l’utilisateur peut effectuer.

La classe CameraPanController est une collection régulièrement actualisée d’informations sur l’état du contrôleur de caméra et permet à notre application d’obtenir ces informations à partir de sa boucle de mise à jour.

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

À présent, créons un en-tête qui définit l’état du contrôleur de caméra, ainsi que les méthodes de base et les gestionnaires d’événements qui implémentent les interactions du contrôleur de caméra.

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

Les champs privés contiennent l’état actuel du contrôleur de caméra. Examinons-les.

  • m_position est la position de la caméra dans l’espace de scène. Dans cet exemple, la valeur de coordonnée z est fixe à 0. Nous pourrions utiliser un DirectX ::XMFLOAT2 pour représenter cette valeur, mais pour les besoins de cet exemple et de cette extensibilité future, nous utilisons un DirectX ::XMFLOAT3. Nous transmettons cette valeur via la propriété get_Position à l’application elle-même afin qu’elle puisse mettre à jour la fenêtre d’affichage en conséquence.
  • m_panInUse est une valeur booléenne qui indique si une opération panoramique est active ; ou, plus précisément, si le lecteur touche l’écran et déplace la caméra.
  • m_panPointerID est un ID unique pour le pointeur. Nous n’allons pas l’utiliser dans l’exemple, mais il est recommandé d’associer votre classe d’état de contrôleur à un pointeur spécifique.
  • m_panFirstDown est le point sur l’écran où le joueur a d’abord touché l’écran ou cliqué sur la souris pendant l’action panoramique de l’appareil photo. Nous utilisons cette valeur ultérieurement pour définir une zone morte pour empêcher la gigue lorsque l’écran est touché, ou si la souris secoue un peu.
  • m_panPointerPosition est le point sur l’écran où le joueur a actuellement déplacé le pointeur. Nous l’utilisons pour déterminer la direction que le joueur voulait déplacer en l’examinant par rapport à m_panFirstDown.
  • m_panCommand est la commande calculée finale pour le contrôleur de caméra : haut, bas, gauche ou droite. Étant donné que nous travaillons avec une caméra fixe sur le plan x-y, il peut s’agir d’une valeur DirectX ::XMFLOAT2 à la place.

Nous utilisons ces 3 gestionnaires d’événements pour mettre à jour les informations d’état du contrôleur de caméra.

  • OnPointerPressed est un gestionnaire d’événements que notre application appelle lorsque les joueurs appuient sur un doigt sur la surface tactile et que le pointeur est déplacé vers les coordonnées de la presse.
  • OnPointerMoved est un gestionnaire d’événements que notre application appelle lorsque le lecteur balaye un doigt sur l’aire tactile. Il est mis à jour avec les nouvelles coordonnées du chemin d’accès glisser.
  • OnPointerReleased est un gestionnaire d’événements que notre application appelle lorsque le lecteur supprime le doigt pressant de l’aire tactile.

Enfin, nous utilisons ces méthodes et propriétés pour initialiser, accéder et mettre à jour les informations d’état du contrôleur de caméra.

  • Initialiser est un gestionnaire d’événements que notre application appelle pour initialiser les contrôles et les attacher à l’objet CoreWindow qui décrit votre fenêtre d’affichage.
  • SetPosition est une méthode que notre application appelle pour définir les coordonnées (x, y et z) de vos contrôles dans l’espace de scène. Notez que notre coordonnée z est 0 dans ce tutoriel.
  • get_Position est une propriété que notre application accède pour obtenir la position actuelle de la caméra dans l’espace de scène. Vous utilisez cette propriété comme moyen de communiquer la position actuelle de la caméra à l’application.
  • get_FixedLookPoint est une propriété que notre application accède pour obtenir le point actuel vers lequel la caméra du contrôleur est exposée. Dans cet exemple, il est verrouillé normalement dans le plan x-y.
  • Update est une méthode qui lit l’état du contrôleur et met à jour la position de la caméra. Vous appelez continuellement cela <à> partir de la boucle principale de l’application pour actualiser les données du contrôleur de caméra et la position de la caméra dans l’espace de scène.

Vous disposez maintenant de tous les composants dont vous avez besoin pour implémenter des contrôles tactiles. Vous pouvez détecter quand et où se sont produits les événements de pointeur tactile ou de souris, ainsi que l’action. Vous pouvez définir la position et l’orientation de la caméra par rapport à l’espace de scène et suivre les modifications. Enfin, vous pouvez communiquer la nouvelle position de la caméra à l’application appelante.

Maintenant, connectons ces éléments ensemble.

Créer les événements tactiles de base

Le répartiteur d’événements Windows Runtime fournit 3 événements que nous voulons que notre application gère :

Ces événements sont implémentés sur le type CoreWindow. Nous partons du principe que vous disposez d’un objet CoreWindow à utiliser. Pour plus d’informations, consultez Comment configurer votre application C++ UWP pour afficher une vue DirectX.

À mesure que ces événements se déclenchent pendant l’exécution de notre application, les gestionnaires mettent à jour les informations d’état du contrôleur de caméra définies dans nos champs privés.

Tout d’abord, nous allons remplir les gestionnaires d’événements de pointeur tactile. Dans le premier gestionnaire d’événements, OnPointerPressed, nous obtenons les coordonnées x-y du pointeur à partir de CoreWindow qui gère notre affichage lorsque l’utilisateur touche l’écran ou clique sur la souris.

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

Nous utilisons ce gestionnaire pour indiquer à l’instance CameraPanController actuelle que le contrôleur de caméra doit être traité comme actif en définissant m_panInUse sur TRUE. De cette façon, lorsque l’application appelle Update , elle utilise les données de position actuelles pour mettre à jour la fenêtre d’affichage.

Maintenant que nous avons établi les valeurs de base pour le mouvement de la caméra lorsque l’utilisateur touche l’écran ou appuie sur un clic dans la fenêtre d’affichage, nous devons déterminer ce qu’il faut faire lorsque l’utilisateur fait glisser l’écran ou déplace la souris avec un bouton enfoncé.

Le gestionnaire d’événements OnPointerMoved se déclenche chaque fois que le pointeur se déplace, à chaque graduation que le joueur le fait glisser sur l’écran. Nous devons garder l’application au courant de l’emplacement actuel du pointeur, et c’est comment nous le faisons.

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

Enfin, nous devons désactiver le comportement panoramique de l’appareil photo lorsque le joueur cesse de toucher l’écran. Nous utilisons OnPointerReleased, qui est appelé lorsque PointerReleased est déclenché, pour définir m_panInUse sur FALSE et désactiver le mouvement panoramique de l’appareil photo et définir l’ID de pointeur sur 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;
}

Initialiser les contrôles tactiles et l’état du contrôleur

Nous allons raccorder les événements et initialiser tous les champs d’état de base du contrôleur de caméra.

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 fait référence à l’instance CoreWindow de l’application en tant que paramètre et inscrit les gestionnaires d’événements que nous avons développés aux événements appropriés sur ce CoreWindow.

Obtention et définition de la position du contrôleur de caméra

Nous allons définir certaines méthodes pour obtenir et définir la position du contrôleur de caméra dans l’espace de scène.

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 est une méthode publique que nous pouvons appeler à partir de notre application si nous devons définir la position du contrôleur de caméra sur un point spécifique.

get_Position est notre propriété publique la plus importante : c’est la façon dont notre application obtient la position actuelle du contrôleur de caméra dans l’espace de scène afin qu’elle puisse mettre à jour la fenêtre d’affichage en conséquence.

get_FixedLookPoint est une propriété publique qui, dans cet exemple, obtient un point d’apparence normal pour le plan x-y. Vous pouvez modifier cette méthode pour utiliser les fonctions trigonométriques, sin et cos, lors du calcul des valeurs de coordonnées x, y et z si vous souhaitez créer des angles obliques supplémentaires pour la caméra fixe.

Mise à jour des informations d’état du contrôleur de caméra

À présent, nous effectuons nos calculs qui convertissent les informations de coordonnées du pointeur suivies dans m_panPointerPosition en nouvelles informations de coordonnées respectives de notre espace de scène 3D. Notre application appelle cette méthode chaque fois que nous actualisons la boucle principale de l’application. Dans celui-ci, nous calculons les nouvelles informations de position que nous voulons transmettre à l’application qui est utilisée pour mettre à jour la matrice d’affichage avant la projection dans la fenêtre d’affichage.


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

}

Étant donné que nous ne voulons pas que le toucher ou la souris gigue fasse de notre caméra un mouvement panoramique, nous définissons une zone morte autour du pointeur avec un diamètre de 32 pixels. Nous avons également une valeur de vitesse, qui dans ce cas est de 1:1 avec la traversée de pixels du pointeur au-delà de la zone morte. Vous pouvez ajuster ce comportement pour ralentir ou accélérer le rythme du mouvement.

Mise à jour de la matrice d’affichage avec la nouvelle position de la caméra

Nous pouvons maintenant obtenir une coordonnée d’espace de scène sur laquelle notre caméra est axée et qui est mise à jour chaque fois que vous indiquez à votre application de le faire (toutes les 60 secondes dans la boucle principale de l’application, par exemple). Ce pseudocode suggère le comportement appelant que vous pouvez implémenter :

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

Félicitations ! Vous avez implémenté un ensemble simple de contrôles tactiles panoramiques de caméra dans votre jeu.