Contrôles move-look pour les jeux

Découvrez comment ajouter des contrôles de déplacement de souris et de clavier traditionnels (également appelés contrôles mouselook) à votre jeu DirectX.

Nous abordons également la prise en charge du déplacement pour les appareils tactiles, avec le contrôleur de déplacement défini comme la section inférieure gauche de l’écran qui se comporte comme une entrée directionnelle, et le contrôleur de regard défini pour le reste de l’écran, avec le centre de la caméra sur la dernière place que le joueur a touché dans cette zone.

S’il s’agit d’un concept de contrôle inconnu pour vous, pensez à cette façon : le clavier (ou la zone d’entrée directionnelle tactile) contrôle vos jambes dans cet espace 3D, et se comporte comme si vos jambes étaient uniquement capables de se déplacer vers l’avant ou vers l’arrière, ou de se strafing gauche et droite. La souris (ou pointeur tactile) contrôle votre tête. Vous utilisez votre tête pour regarder dans une direction - gauche ou droite, vers le haut ou vers le bas, ou quelque part dans cet avion. S’il existe une cible dans votre vue, vous utiliseriez la souris pour centrer votre vue caméra sur cette cible, puis appuyez sur la touche vers l’avant pour vous déplacer vers celle-ci, ou revenir pour vous éloigner de celle-ci. Pour cercler la cible, vous devez garder la vue de l’appareil photo centrée sur la cible, et déplacer vers la gauche ou la droite en même temps. Vous pouvez voir comment il s’agit d’une méthode de contrôle très efficace pour naviguer dans des environnements 3D !

Ces contrôles sont couramment appelés contrôles WASD dans les jeux, où les touches W, A, S et D sont utilisées pour le mouvement de caméra fixe x-z, et la souris est utilisée pour contrôler la rotation de la caméra autour des axes x et y.

Objectifs

  • Ajoutez des contrôles d’apparence de déplacement de base à votre jeu DirectX pour la souris et le clavier et les écrans tactiles.
  • Implémentez une caméra de première personne utilisée pour naviguer dans un environnement 3D.

Remarque sur les implémentations de contrôle tactile

Pour les contrôles tactiles, nous implémentons deux contrôleurs : le contrôleur de déplacement, qui gère le mouvement dans le plan x-z par rapport au point d’apparence de la caméra ; et le contrôleur de regard, qui vise le point d’apparence de l’appareil photo. Notre contrôleur de déplacement est mappé aux boutons WASD clavier et le contrôleur de regard est mappé à la souris. Toutefois, pour les contrôles tactiles, nous devons définir une région de l’écran qui sert d’entrées directionnelles ou de boutons WASD virtuels, avec le reste de l’écran servant d’espace d’entrée pour les contrôles d’apparence.

Notre écran ressemble à ceci.

disposition du contrôleur move-look

Lorsque vous déplacez le pointeur tactile (pas la souris !) en bas à gauche de l’écran, tout mouvement vers le haut fait avancer l’appareil photo. Tout mouvement vers le bas rend l’appareil photo se déplacer vers l’arrière. Il en va de même pour le mouvement gauche et droit à l’intérieur de l’espace pointeur du contrôleur de déplacement. En dehors de cet espace, et il devient un contrôleur de look - vous vous contentez de toucher ou faites glisser la caméra vers l’endroit où vous souhaitez qu’elle soit face.

Configurer l’infrastructure d’événements d’entrée de base

Tout d’abord, nous devons créer notre classe de contrôle que nous utilisons pour gérer les événements d’entrée à partir de la souris et du clavier, et mettre à jour la perspective de la caméra en fonction de cette entrée. Comme nous implémentons des contrôles move-look, nous l’appelons MoveLookController.

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 MoveLookController
{
};  // class MoveLookController

À présent, créons un en-tête qui définit l’état du contrôleur move-look et sa caméra de première personne, ainsi que les méthodes de base et les gestionnaires d’événements qui implémentent les contrôles et qui mettent à jour l’état de la caméra.

#define ROTATION_GAIN 0.004f    // Sensitivity adjustment for the look controller
#define MOVEMENT_GAIN 0.1f      // Sensitivity adjustment for the move controller

ref class MoveLookController
{
private:
    // Properties of the controller object
    DirectX::XMFLOAT3 m_position;               // The position of the controller
    float m_pitch, m_yaw;           // Orientation euler angles in radians

    // Properties of the Move control
    bool m_moveInUse;               // Specifies whether the move control is in use
    uint32 m_movePointerID;         // Id of the pointer in this control
    DirectX::XMFLOAT2 m_moveFirstDown;          // Point where initial contact occurred
    DirectX::XMFLOAT2 m_movePointerPosition;   // Point where the move pointer is currently located
    DirectX::XMFLOAT3 m_moveCommand;            // The net command from the move control

    // Properties of the Look control
    bool m_lookInUse;               // Specifies whether the look control is in use
    uint32 m_lookPointerID;         // Id of the pointer in this control
    DirectX::XMFLOAT2 m_lookLastPoint;          // Last point (from last frame)
    DirectX::XMFLOAT2 m_lookLastDelta;          // For smoothing

    bool m_forward, m_back;         // States for movement
    bool m_left, m_right;
    bool m_up, m_down;


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

    void OnKeyDown(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::KeyEventArgs^ args
        );

    void OnKeyUp(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::KeyEventArgs^ args
        );

    // Set up the Controls that this controller supports
    void Initialize( _In_ Windows::UI::Core::CoreWindow^ window );

    void Update( Windows::UI::Core::CoreWindow ^window );
    
internal:
    // Accessor to set position of controller
    void SetPosition( _In_ DirectX::XMFLOAT3 pos );

    // Accessor to set position of controller
    void SetOrientation( _In_ float pitch, _In_ float yaw );

    // Returns the position of the controller object
    DirectX::XMFLOAT3 get_Position();

    // Returns the point  which the controller is facing
    DirectX::XMFLOAT3 get_LookPoint();


};  // class MoveLookController

Notre code contient 4 groupes de champs privés. Examinons l’objectif de chacun d’eux.

Tout d’abord, nous définissons des champs utiles qui contiennent nos informations mises à jour sur notre vue caméra.

  • m_position est la position de la caméra (et par conséquent le plan d’affichage) dans la scène 3D, à l’aide de coordonnées de scène.
  • m_pitch est la hauteur de la caméra, ou sa rotation vers le haut autour de l’axe x du plan d’affichage, en radians.
  • m_yaw est le lacet de la caméra, ou sa rotation gauche droite autour de l’axe y du plan de vue, en radians.

À présent, définissons les champs que nous utilisons pour stocker des informations sur l’état et la position de nos contrôleurs. Tout d’abord, nous allons définir les champs dont nous avons besoin pour notre contrôleur de déplacement tactile. (Il n’y a rien de spécial nécessaire pour l’implémentation du clavier du contrôleur de déplacement. Nous allons simplement lire les événements clavier avec des gestionnaires spécifiques.)

  • m_moveInUse indique si le contrôleur de déplacement est en cours d’utilisation.
  • m_movePointerID est l’ID unique du pointeur de déplacement actuel. Nous l’utilisons pour différencier le pointeur d’apparence et le pointeur de déplacement lorsque nous vérifions la valeur de l’ID du pointeur.
  • m_moveFirstDown est le point sur l’écran où le joueur a d’abord touché la zone de pointeur du contrôleur de déplacement. Nous utilisons cette valeur plus tard pour définir une zone morte pour empêcher les mouvements minuscules de la vue.
  • m_movePointerPosition est le point à l’écran vers lequel le joueur a déplacé le pointeur. Nous l’utilisons pour déterminer la direction que le joueur voulait déplacer en l’examinant par rapport à m_moveFirstDown.
  • m_moveCommand est la commande calculée finale pour le contrôleur de déplacement : haut (avant), bas (arrière), gauche ou droite.

À présent, nous définissons les champs que nous utilisons pour notre contrôleur de recherche, à la fois les implémentations de souris et tactiles.

  • m_lookInUse indique si le contrôle d’apparence est en cours d’utilisation.
  • m_lookPointerID est l’ID unique du pointeur d’apparence actuel. Nous l’utilisons pour différencier le pointeur d’apparence et le pointeur de déplacement lorsque nous vérifions la valeur de l’ID du pointeur.
  • m_lookLastPoint est le dernier point, dans les coordonnées de scène, qui a été capturé dans le cadre précédent.
  • m_lookLastDelta est la différence calculée entre la m_position actuelle et la m_lookLastPoint.

Enfin, nous définissons 6 valeurs booléennes pour les 6 degrés de mouvement, que nous utilisons pour indiquer l’état actuel de chaque action de déplacement directionnel (activé ou désactivé) :

  • m_forward, m_back, m_left, m_right, m_up et m_down.

Nous utilisons les 6 gestionnaires d’événements pour capturer les données d’entrée que nous utilisons pour mettre à jour l’état de nos contrôleurs :

  • OnPointerPressed. Le joueur a appuyé sur le bouton gauche de la souris avec le pointeur dans notre écran de jeu ou a touché l’écran.
  • OnPointerMoved. Le joueur a déplacé la souris avec le pointeur dans notre écran de jeu, ou a fait glisser le pointeur tactile sur l’écran.
  • OnPointerReleased. Le joueur a libéré le bouton gauche de la souris avec le pointeur dans notre écran de jeu ou arrêté de toucher l’écran.
  • OnKeyDown. Le lecteur a appuyé sur une touche.
  • OnKeyUp. Le joueur a libéré une clé.

Enfin, nous utilisons ces méthodes et propriétés pour initialiser, accéder et mettre à jour les informations d’état des contrôleurs.

  • Initialiser. Notre application appelle ce gestionnaire d’événements pour initialiser les contrôles et les attacher à l’objet CoreWindow qui décrit notre fenêtre d’affichage.
  • SetPosition. Notre application appelle cette méthode pour définir les coordonnées (x, y et z) de nos contrôles dans l’espace de scène.
  • SetOrientation. Notre application appelle cette méthode pour définir la hauteur et le lacet de la caméra.
  • get_Position. Notre application accède à cette propriété pour obtenir la position actuelle de la caméra dans l’espace de scène. Vous utilisez cette propriété comme méthode de communication de la position actuelle de la caméra à l’application.
  • get_LookPoint. Notre application accède à cette propriété pour obtenir le point actuel vers lequel la caméra du contrôleur est exposée.
  • Mise à jour. Lit l’état du déplacement et de l’apparence des contrôleurs et met à jour la position de l’appareil photo. Vous appelez continuellement cette méthode à 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 vos contrôles move-look. Nous allons donc connecter ces pièces ensemble.

Créer les événements d’entrée de base

Le répartiteur d’événements Windows Runtime fournit 5 événements que nous voulons que les instances de la classe MoveLookController gèrent :

Ces événements sont implémentés sur le type CoreWindow. Nous partons du principe que vous disposez d’un objet CoreWindow à utiliser. Si vous ne savez pas comment en obtenir un, consultez Comment configurer votre application C++ (UWP) plateforme Windows universelle 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 des contrôleurs définies dans nos champs privés.

Tout d’abord, nous allons remplir les gestionnaires d’événements de pointeur tactile et de souris. 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 clique sur la souris ou touche l’écran dans la région du contrôleur de regard.

OnPointerPressed

void MoveLookController::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 ( deviceType == PointerDeviceType::Mouse )
    {
        // Action, Jump, or Fire
    }

    // Check  if this pointer is in the move control.
    // Change the values  to percentages of the preferred screen resolution.
    // You can set the x value to <preferred resolution> * <percentage of width>
    // for example, ( position.x < (screenResolution.x * 0.15) ).

    if (( position.x < 300 && position.y > 380 ) && ( deviceType != PointerDeviceType::Mouse ))
    {
        if ( !m_moveInUse ) // if no pointer is in this control yet
        {
            // Process a DPad touch down event.
            m_moveFirstDown = position;                 // Save the location of the initial contact.
            m_movePointerPosition = position;
            m_movePointerID = pointerID;                // Store the id of the pointer using this control.
            m_moveInUse = TRUE;
        }
    }
    else // This pointer must be in the look control.
    {
        if ( !m_lookInUse ) // If no pointer is in this control yet...
        {
            m_lookLastPoint = position;                         // save the point for later move
            m_lookPointerID = args->CurrentPoint->PointerId;  // store the id of pointer using this control
            m_lookLastDelta.x = m_lookLastDelta.y = 0;          // these are for smoothing
            m_lookInUse = TRUE;
        }
    }
}

Ce gestionnaire d’événements vérifie si le pointeur n’est pas la souris (à des fins de cet exemple, qui prend en charge à la fois la souris et l’interaction tactile) et s’il se trouve dans la zone du contrôleur de déplacement. Si les deux critères sont vrais, il vérifie si le pointeur vient d’être appuyé, en particulier si ce clic n’est pas lié à un déplacement précédent ou à une entrée d’apparence, en testant si m_moveInUse a la valeur false. Si tel est le cas, le gestionnaire capture le point dans la zone du contrôleur de déplacement où la pression s’est produite et définit m_moveInUse la valeur true, de sorte que lorsque ce gestionnaire est appelé à nouveau, il ne remplacera pas la position de début de l’interaction d’entrée du contrôleur de déplacement. Il met également à jour l’ID de pointeur du contrôleur de déplacement vers l’ID du pointeur actuel.

Si le pointeur est la souris ou si le pointeur tactile ne se trouve pas dans la zone du contrôleur de déplacement, il doit se trouver dans la zone du contrôleur de recherche. Il définit m_lookLastPoint à la position actuelle où l’utilisateur a appuyé sur le bouton de la souris ou enfoncé et enfoncé, réinitialise le delta et met à jour l’ID de pointeur du contrôleur de recherche sur l’ID de pointeur actuel. Il définit également l’état du contrôleur d’apparence sur actif.

OnPointerMoved

void MoveLookController::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);

    // Decide which control this pointer is operating.
    if (pointerID == m_movePointerID)           // This is the move pointer.
    {
        // Move control
        m_movePointerPosition = position;       // Save the current position.

    }
    else if (pointerID == m_lookPointerID)      // This is the look pointer.
    {
        // Look control

        DirectX::XMFLOAT2 pointerDelta;
        pointerDelta.x = position.x - m_lookLastPoint.x;        // How far did pointer move
        pointerDelta.y = position.y - m_lookLastPoint.y;

        DirectX::XMFLOAT2 rotationDelta;
        rotationDelta.x = pointerDelta.x * ROTATION_GAIN;   // Scale for control sensitivity.
        rotationDelta.y = pointerDelta.y * ROTATION_GAIN;

        m_lookLastPoint = position;                     // Save for the next time through.

                                                        // Update our orientation based on the command.
        m_pitch -= rotationDelta.y;                     // Mouse y increases down, but pitch increases up.
        m_yaw -= rotationDelta.x;                       // Yaw is defined as CCW around the y-axis.

                                                        // Limit the pitch to straight up or straight down.
        m_pitch = (float)__max(-DirectX::XM_PI / 2.0f, m_pitch);
        m_pitch = (float)__min(+DirectX::XM_PI / 2.0f, m_pitch);
    }
}

Le gestionnaire d’événements OnPointerMoved se déclenche chaque fois que le pointeur se déplace (dans ce cas, si un pointeur d’écran tactile est déplacé ou si le pointeur de la souris est déplacé pendant que le bouton gauche est enfoncé). Si l’ID de pointeur est identique à l’ID du pointeur du contrôleur de déplacement, il s’agit du pointeur de déplacement ; sinon, nous vérifions s’il s’agit du contrôleur d’apparence qui est le pointeur actif.

S’il s’agit du contrôleur de déplacement, nous mettons simplement à jour la position du pointeur. Nous continuons à le mettre à jour tant que l’événement PointerMoved continue de se déclencher, car nous voulons comparer la position finale avec le premier que nous avons capturé avec le gestionnaire d’événements OnPointerPressed.

Si c’est le contrôleur de regard, les choses sont un peu plus compliquées. Nous devons calculer un nouveau point d’apparence et centrer la caméra dessus, donc nous calculons le delta entre le dernier point d’apparence et la position actuelle de l’écran, puis nous multiplions par rapport à notre facteur d’échelle, que nous pouvons ajuster pour rendre les mouvements d’apparence plus petits ou plus grands par rapport à la distance du mouvement de l’écran. À l’aide de cette valeur, nous calculons la hauteur et le lacet.

Enfin, nous devons désactiver les comportements du contrôleur de déplacement ou d’apparence lorsque le joueur cesse de déplacer la souris ou de toucher l’écran. Nous utilisons OnPointerReleased, que nous appelons lorsque PointerReleased est déclenché, pour définir m_moveInUse ou m_lookInUse sur FALSE et désactiver le mouvement panoramique de l’appareil photo, et pour zéro l’ID de pointeur.

OnPointerReleased

void MoveLookController::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 );


    if ( pointerID == m_movePointerID )    // This was the move pointer.
    {
        m_moveInUse = FALSE;
        m_movePointerID = 0;
    }
    else if (pointerID == m_lookPointerID ) // This was the look pointer.
    {
        m_lookInUse = FALSE;
        m_lookPointerID = 0;
    }
}

Jusqu’à présent, nous avons géré tous les événements d’écran tactile. À présent, gérons les événements d’entrée de touche pour un contrôleur de déplacement basé sur le clavier.

OnKeyDown

void MoveLookController::OnKeyDown(
                                   __in CoreWindow^ sender,
                                   __in KeyEventArgs^ args )
{
    Windows::System::VirtualKey Key;
    Key = args->VirtualKey;

    // Figure out the command from the keyboard.
    if ( Key == VirtualKey::W )     // Forward
        m_forward = true;
    if ( Key == VirtualKey::S )     // Back
        m_back = true;
    if ( Key == VirtualKey::A )     // Left
        m_left = true;
    if ( Key == VirtualKey::D )     // Right
        m_right = true;
}

Tant que l’une de ces touches est enfoncée, ce gestionnaire d’événements définit l’état de déplacement directionnel correspondant sur true.

OnKeyUp

void MoveLookController::OnKeyUp(
                                 __in CoreWindow^ sender,
                                 __in KeyEventArgs^ args)
{
    Windows::System::VirtualKey Key;
    Key = args->VirtualKey;

    // Figure out the command from the keyboard.
    if ( Key == VirtualKey::W )     // forward
        m_forward = false;
    if ( Key == VirtualKey::S )     // back
        m_back = false;
    if ( Key == VirtualKey::A )     // left
        m_left = false;
    if ( Key == VirtualKey::D )     // right
        m_right = false;
}

Et lorsque la clé est libérée, ce gestionnaire d’événements la définit sur false. Lorsque nous appelons Update, il vérifie ces états de déplacement directionnel et déplace l’appareil photo en conséquence. C’est un peu plus simple que l’implémentation tactile !

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

Nous allons maintenant raccorder les événements et initialiser tous les champs d’état du contrôleur.

Initialize

void MoveLookController::Initialize( _In_ CoreWindow^ window )
{

    // Opt in to receive touch/mouse events.
    window->PointerPressed += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerPressed);

    window->PointerMoved += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerMoved);

    window->PointerReleased += 
    ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MoveLookController::OnPointerReleased);

    window->CharacterReceived +=
    ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &MoveLookController::OnCharacterReceived);

    window->KeyDown += 
    ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &MoveLookController::OnKeyDown);

    window->KeyUp += 
    ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &MoveLookController::OnKeyUp);

    // Initialize the state of the controller.
    m_moveInUse = FALSE;                // No pointer is in the Move control.
    m_movePointerID = 0;

    m_lookInUse = FALSE;                // No pointer is in the Look control.
    m_lookPointerID = 0;

    //  Need to init this as it is reset every frame.
    m_moveCommand = DirectX::XMFLOAT3( 0.0f, 0.0f, 0.0f );

    SetOrientation( 0, 0 );             // Look straight ahead when the app starts.

}

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. Il initialise les ID du pointeur de déplacement et d’apparence, définit le vecteur de commande pour l’implémentation du contrôleur de déplacement d’écran tactile sur zéro et définit l’appareil photo à l’avance lorsque l’application démarre.

Obtention et définition de la position et de l’orientation de la caméra

Définissons quelques méthodes pour obtenir et définir la position de la caméra par rapport à la fenêtre d’affichage.

void MoveLookController::SetPosition( _In_ DirectX::XMFLOAT3 pos )
{
    m_position = pos;
}

// Accessor to set the position of the controller.
void MoveLookController::SetOrientation( _In_ float pitch, _In_ float yaw )
{
    m_pitch = pitch;
    m_yaw = yaw;
}

// Returns the position of the controller object.
DirectX::XMFLOAT3 MoveLookController::get_Position()
{
    return m_position;
}

// Returns the point at which the camera controller is facing.
DirectX::XMFLOAT3 MoveLookController::get_LookPoint()
{
    float y = sinf(m_pitch);        // Vertical
    float r = cosf(m_pitch);        // In the plane
    float z = r*cosf(m_yaw);        // Fwd-back
    float x = r*sinf(m_yaw);        // Left-right
    DirectX::XMFLOAT3 result(x,y,z);
    result.x += m_position.x;
    result.y += m_position.y;
    result.z += m_position.z;

    // Return m_position + DirectX::XMFLOAT3(x, y, z);
    return result;
}

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

À présent, nous effectuons nos calculs qui convertissent les informations de coordonnées du pointeur suivies dans m_movePointerPosition en nouvelles informations de coordonnées respectives de notre système de coordonnées mondial. Notre application appelle cette méthode chaque fois que nous actualisons la boucle principale de l’application. Il est donc ici que nous calculons les nouvelles informations de position de point d’apparence que nous voulons transmettre à l’application pour mettre à jour la matrice d’affichage avant la projection dans la fenêtre d’affichage.

void MoveLookController::Update(CoreWindow ^window)
{
    // Check for input from the Move control.
    if (m_moveInUse)
    {
        DirectX::XMFLOAT2 pointerDelta(m_movePointerPosition);
        pointerDelta.x -= m_moveFirstDown.x;
        pointerDelta.y -= m_moveFirstDown.y;

        // Figure out the command from the touch-based virtual joystick.
        if (pointerDelta.x > 16.0f)      // Leave 32 pixel-wide dead spot for being still.
            m_moveCommand.x = 1.0f;
        else
            if (pointerDelta.x < -16.0f)
            m_moveCommand.x = -1.0f;

        if (pointerDelta.y > 16.0f)      // Joystick y is up, so change sign.
            m_moveCommand.y = -1.0f;
        else
            if (pointerDelta.y < -16.0f)
            m_moveCommand.y = 1.0f;
    }

    // Poll our state bits that are set by the keyboard input events.
    if (m_forward)
        m_moveCommand.y += 1.0f;
    if (m_back)
        m_moveCommand.y -= 1.0f;

    if (m_left)
        m_moveCommand.x -= 1.0f;
    if (m_right)
        m_moveCommand.x += 1.0f;

    if (m_up)
        m_moveCommand.z += 1.0f;
    if (m_down)
        m_moveCommand.z -= 1.0f;

    // Make sure that 45 degree cases are not faster.
    DirectX::XMFLOAT3 command = m_moveCommand;
    DirectX::XMVECTOR vector;
    vector = DirectX::XMLoadFloat3(&command);

    if (fabsf(command.x) > 0.1f || fabsf(command.y) > 0.1f || fabsf(command.z) > 0.1f)
    {
        vector = DirectX::XMVector3Normalize(vector);
        DirectX::XMStoreFloat3(&command, vector);
    }
    

    // Rotate command to align with our direction (world coordinates).
    DirectX::XMFLOAT3 wCommand;
    wCommand.x = command.x*cosf(m_yaw) - command.y*sinf(m_yaw);
    wCommand.y = command.x*sinf(m_yaw) + command.y*cosf(m_yaw);
    wCommand.z = command.z;

    // Scale for sensitivity adjustment.
    wCommand.x = wCommand.x * MOVEMENT_GAIN;
    wCommand.y = wCommand.y * MOVEMENT_GAIN;
    wCommand.z = wCommand.z * MOVEMENT_GAIN;

    // Our velocity is based on the command.
    // Also note that y is the up-down axis. 
    DirectX::XMFLOAT3 Velocity;
    Velocity.x = -wCommand.x;
    Velocity.z = wCommand.y;
    Velocity.y = wCommand.z;

    // Integrate
    m_position.x += Velocity.x;
    m_position.y += Velocity.y;
    m_position.z += Velocity.z;

    // Clear movement input accumulator for use during the next frame.
    m_moveCommand = DirectX::XMFLOAT3(0.0f, 0.0f, 0.0f);

}

Étant donné que nous ne voulons pas de mouvement gigue lorsque le joueur utilise notre contrôleur de déplacement tactile, nous définissons une zone morte virtuelle autour du pointeur avec un diamètre de 32 pixels. Nous ajoutons également la vitesse, qui est la valeur de commande plus un taux de gain de mouvement. (Vous pouvez ajuster ce comportement à votre goût, ralentir ou accélérer la vitesse de mouvement en fonction de la distance que le pointeur déplace dans la zone du contrôleur de déplacement.)

Lorsque nous calculons la vitesse, nous traduisez également les coordonnées reçues du déplacement et regardez les contrôleurs dans le mouvement du point d’apparence réel que nous envoyons à la méthode qui calcule notre matrice de vue pour la scène. Tout d’abord, nous inversons la coordonnée x, car si nous cliquez sur déplacer ou faire glisser vers la gauche ou la droite avec le contrôleur d’apparence, le point d’apparence pivote dans la direction opposée de la scène, car une caméra peut basculer sur son axe central. Ensuite, nous permutons les axes y et z, car une touche haut/bas appuie sur la touche ou le mouvement de glissement tactile (lus sous la forme d’un comportement de l’axe y) sur le contrôleur de déplacement doit se traduire en une action de caméra qui déplace le point de regard vers ou hors de l’écran (l’axe z).

La position finale du point de recherche pour le joueur est la dernière position plus la vitesse calculée, et c’est ce qui est lu par le renderer lorsqu’il appelle la méthode get_Position (probablement pendant la configuration de chaque image). Après cela, nous réinitialisons la commande move sur zéro.

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

Nous pouvons obtenir une coordonnée d’espace de scène sur laquelle notre appareil photo est concentré et qui est mis à 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 :

myMoveLookController->Update( m_window );   

// Update the view matrix based on the camera position.
myFirstPersonCamera->SetViewParameters(
                 myMoveLookController->get_Position(),       // Point we are at
                 myMoveLookController->get_LookPoint(),      // Point to look towards
                 DirectX::XMFLOAT3( 0, 1, 0 )                   // Up-vector
                 ); 

Félicitations ! Vous avez implémenté des contrôles de déplacement de base pour les écrans tactiles et les contrôles tactiles d’entrée clavier/souris dans votre jeu !