Partager via


Volant de course et retour de force

Cet article explique les notions de base de la programmation pour les volants de course sur Xbox One avec l’API Windows.Gaming.Input.RacingWheel et les API associées pour la plateforme Windows universelle (UWP).

Voici ce que vous allez apprendre à la lecture de cet article :

  • comment rassembler une liste des volants de course connectés et de leurs utilisateurs
  • comment détecter qu’un volant de course a été ajouté ou supprimé
  • comment lire les entrées d’un ou de plusieurs volants de course
  • comment envoyer des commandes de retour de force
  • comment les volants de course se comportent en tant qu’appareils de navigation de l’interface utilisateur

Vue d’ensemble des volants de course

Les volants de course sont des périphériques d’entrée qui ressemblent à la sensation d’un véritable cockpit de voiture de course. Les volants de course sont le périphérique d’entrée parfait pour les jeux de course de style arcade et de simulation qui présentent des voitures ou des camions. Les volants de course sont pris en charge dans Windows 10 ou Windows 11 et les applications UWP Xbox One par l’espace de noms Windows.Gaming.Input.

Il existe différentes gammes de prix pour les volants de course. Les plus chers présentent généralement des fonctionnalités de retour de force et d’entrée plus performantes et en plus grand nombre. Tous les volants de course sont équipés d’un volant de direction analogique, de commandes d’accélération et de frein analogiques, et de quelques boutons sur le volant. Certains volants de course sont également équipés de commandes d’embrayage et de frein à main analogiques, de leviers de vitesse et de fonctionnalités de retour de force. Cependant, les volants de course ne sont pas tous équipés du même ensemble de fonctionnalités. De plus, il se peut qu’ils ne prennent pas tous en charge les mêmes fonctions ; par exemple, certains volants peuvent accepter des plages de rotation différentes et les leviers de vitesses peuvent gérer différents nombres de vitesses.

Fonctionnalités de l’appareil

Différents volants de course proposent un ensemble de fonctionnalités de périphérique en option et des niveaux divers de prise en charge de ces fonctionnalités. Ce degré de variation entre un type de périphérique d’entrée est unique pour tous les périphériques pris en charge par l’API Windows.Gaming.Input. En outre, la plupart des appareils que vous rencontrerez prennent en charge au moins certaines fonctionnalités facultatives ou d’autres variantes. En raison de cela, il est important de déterminer les fonctionnalités de chaque volant de course connecté individuellement et de prendre en charge la variation complète des fonctionnalités qui sont logiques pour votre jeu.

Pour plus d’informations, consultez Déterminer les fonctionnalités des volants de course.

Retour de force

Certains volants de course proposent un retour de force réel ; ils peuvent appliquer des forces réelles sur un axe de contrôle, comme un volant, et non une simple vibration. Les jeux utilisent cette capacité pour créer une meilleure immersion (dommages simulés à l’accident, « sensation de route ») et pour augmenter le défi de bien conduire.

Pour plus d’informations, consultez Vue d’ensemble du retour de force.

Navigation dans l’interface utilisateur

Pour réduire la difficulté associée à la prise en charge de plusieurs périphériques d’entrée différents lors de la navigation dans l’interface utilisateur, et pour favoriser la cohérence entre les jeux et les périphériques, la plupart des périphériques d’entrée physiques agissent en tant que périphérique d’entrée logique distinct, appelé contrôleur de navigation de l’interface utilisateur. Le contrôleur de navigation de l’interface utilisateur fournit un vocabulaire commun pour les commandes de navigation de l’interface utilisateur sur les périphériques d’entrée.

En raison de leur focus unique sur les contrôles analogiques et le degré de variation entre différents volants de course, ils sont généralement équipés d’un bouton directionnel, de boutons Affichage, Menu, A, B, X et Y qui ressemblent à ceux d’un boîtier de commande ; ces boutons ne sont pas destinés à prendre en charge les commandes de gameplay et ne peuvent pas être facilement accessibles en tant que boutons de volant de course.

En tant que contrôleurs de navigation de l’interface utilisateur, les volants de course mappent l’ensemble requis de commandes de navigation sur le stick analogique gauche, le bouton directionnel et les boutons Afficher, Menu, A et B.

Commande de navigation Entrée de volant de course
Haut Bouton directionnel vers le haut
Descendre Bouton directionnel vers le bas
Left Bouton directionnel vers la gauche
Right Bouton directionnel vers la droite
Afficher Bouton Afficher
Menu Bouton Menu
Accepter Bouton A
Annuler Bouton B

En outre, certains volants de course peuvent mapper certaines des commandes facultatives de navigation à d’autres entrées qu’ils prennent en charge, mais les mappages de commandes peuvent varier d’un appareil à l’autre. Envisagez également de prendre en charge ces commandes, mais assurez-vous que ces commandes ne sont pas essentielles pour naviguer dans l’interface de votre jeu.

Commande de navigation Entrée de volant de course
Page précédente varie
Page suivante varie
Page vers la gauche varie
Page vers la droite varie
Faire défiler vers le haut varie
Faire défiler vers le bas varie
Faire défiler vers la gauche varie
Faire défiler vers la droite varie
Contexte 1 Bouton X (en général)
Contexte 2 Bouton Y (en général)
Contexte 3 varie
Contexte 4 varie

Détecter et suivre les volants de course

La détection et le suivi des volants de course fonctionnent exactement de la même façon que pour les boîtiers de commande, mais avec la classe RacingWheel au lieu de la classe Gamepad. Pour plus d’informations, consultez Boîtier de commande et vibrations.

Lecture du volant de course

Une fois que vous avez identifié les volants de course qui vous intéressent, vous êtes prêt à recueillir des entrées à partir d’eux. Toutefois, contrairement à d’autres sortes d’entrées que vous connaissez peut-être, les volants de course ne communiquent pas les changements d’état en déclenchant des événements. Au lieu de cela, vous prenez des lectures régulières de leurs états actuels en les interrogeant.

Interrogation du volant de course

Le processus d’interrogation capture un instantané du volant de course à un moment précis. Cette approche de la collecte d’entrée est adaptée à la plupart des jeux, car leur logique s’exécute généralement dans une boucle déterministe plutôt que d’être pilotée par les événements. Il est également généralement plus simple d’interpréter les commandes de jeu à partir d’entrées collectées toutes en même temps plutôt que de nombreuses entrées uniques collectées au fil du temps.

Vous interrogez un volant de course en appelant GetCurrentReading ; cette fonction retourne un RacingWheelReading qui contient l’état du volant de course.

L’exemple de code suivant interroge un volant de course pour obtenir son état actuel.

auto racingwheel = myRacingWheels[0];

RacingWheelReading reading = racingwheel->GetCurrentReading();

En plus de l’état du volant de course, chaque valeur comprend un timestamp qui indique précisément le moment d’extraction de cet état. Le timestamp est utile pour se rapporter au minutage des lectures précédentes ou au minutage de la simulation de jeu.

Détermination des fonctionnalités du volant de course

La plupart des contrôles du volant de course sont facultatifs ou prennent en charge différentes variantes même dans les contrôles requis, de sorte que vous devez déterminer les capacités de chaque volant de course individuellement avant de pouvoir traiter l’entrée collectée dans chaque lecture du volant de course.

Les commandes facultatives sont le frein à main, l’embrayage et le levier de vitesse ; vous pouvez déterminer si un volant de course connecté prend en charge ces contrôles en lisant les propriétés HasHandbrake, HasClutch et HasPatternShifter, respectivement. Le contrôle est pris en charge si la valeur de la propriété est true ; sinon, elle n’est pas prise en charge.

if (racingwheel->HasHandbrake)
{
    // the handbrake is supported
}

if (racingwheel->HasClutch)
{
    // the clutch is supported
}

if (racingwheel->HasPatternShifter)
{
    // the pattern shifter is supported
}

En outre, les commandes qui peuvent varier sont le volant de direction et le levier de vitesse. Le volant de direction peut varier selon le degré de rotation physique que le volant réel peut prendre en charge, tandis que le levier de vitesse peut varier en fonction du nombre de rapports de vitesse de marche avant distinctes qu’il prend en charge. Vous pouvez déterminer le plus grand angle de rotation pris en charge par le volant réel en lisant la propriété MaxWheelAngle du volant de course ; sa valeur est l’angle physique maximal pris en charge dans le sens horaire (positif), qui est également pris en charge dans le sens anti-horaire (degrés négatifs). Vous pouvez déterminer le plus grand rapport de vitesse de marche avant pris en charge par le sélecteur de modèle en lisant la propriété MaxPatternShifterGear du volant de course ; sa valeur est le rapport de vitesse de marche avant le plus élevé pris en charge, inclusif, c’est-à-dire, si sa valeur est 4, alors le levier de vitesse prend en charge la marche arrière, le point mort, la première, la deuxième, la troisième et la quatrième vitesse.

auto maxWheelDegrees = racingwheel->MaxWheelAngle;
auto maxShifterGears = racingwheel->MaxPatternShifterGear;

Enfin, certains volants de course prennent en charge les retours de force par le volant. Vous pouvez déterminer si un volant de course connecté prend en charge le retour de force en lisant la propriété WheelMotor du volant de course. Le retour de force est pris en charge si WheelMotor n’est pas null ; sinon, il n’est pas pris en charge.

if (racingwheel->WheelMotor != nullptr)
{
    // force feedback is supported
}

Pour plus d’informations sur l’utilisation de la fonctionnalité de retour de force des volants de course qui le prennent en charge, consultez Vue d’ensemble du retour de force.

Lecture des boutons

Chacun des boutons du volant de course (les quatre directions du bouton directionnel, les boutons Vitesse inférieure et Vitesse supérieure et les 16 boutons supplémentaires) fournit une lecture numérique qui indique s’il est appuyé (bas) ou relâché (haut). Pour plus d’efficacité, les lectures des boutons ne sont pas représentées individuellement sous forme de valeurs booléennes. Elles sont toutes regroupées dans un seul champ de bits représenté par l’énumération RacingWheelButtons.

Remarque

Les volants de course sont équipés de boutons supplémentaires utilisés pour la navigation de l’interface utilisateur, tels que les boutons Affichage et Menu. Ces boutons ne figurent pas dans l’énumération RacingWheelButtons. Leurs entrées sont lues uniquement quand le volant de course est utilisé comme périphérique de navigation de l’interface utilisateur. Pour plus d’informations, consultez Appareil de navigation de l’interface utilisateur.

Les valeurs des boutons sont lues à partir de la propriété Buttons de la structure RacingWheelReading. Comme cette propriété est un champ de bits, un masquage au niveau du bit est effectué pour isoler la valeur du bouton qui vous intéresse. Le bouton est appuyé (bas) lorsque le bit correspondant est défini ; sinon, il est relâché (haut).

L’exemple suivant détermine si le bouton Vitesse supérieure est à l’état appuyé.

if (RacingWheelButtons::NextGear == (reading.Buttons & RacingWheelButtons::NextGear))
{
    // Next Gear is pressed
}

L’exemple suivant détermine si le bouton Vitesse supérieure est à l’état relâché.

if (RacingWheelButtons::None == (reading.Buttons & RacingWheelButtons::NextGear))
{
    // Next Gear is released (not pressed)
}

Vous pouvez avoir besoin de savoir quand un bouton passe de l’état appuyé à l’état relâché, ou inversement, si plusieurs boutons sont à l’état appuyé ou relâché, ou si un groupe de boutons a une disposition particulière, certains présentant l’état appuyé et d’autres l’état relâché. Pour plus d’informations sur la détection de ces conditions, consultez Détection des transitions de boutons et Détection des dispositions complexes des boutons.

Lecture du volant

Le volant de direction est un contrôle requis qui fournit une lecture analogique comprise entre -1,0 et +1,0. Une valeur de -1,0 correspond à la position de volant la plus à gauche ; une valeur de +1,0 indique la position la plus à droite. La valeur du volant est lue à partir de la propriété Wheel de la structure RacingWheelReading.

float wheel = reading.Wheel;  // returns a value between -1.0 and +1.0.

Bien que les lectures du volant correspondent à différents degrés de rotation physique du volant réel en fonction de la plage de rotation prise en charge par le volant de course physique, vous ne souhaitez généralement pas mettre à l’échelle les lectures de volant ; les volants qui prennent en charge des degrés de rotation plus importants fournissent simplement une plus grande précision.

Lecture de l’accélérateur et du frein

L’accélérateur et le frein sont des contrôles obligatoires qui fournissent chacun des lectures analogiques comprises entre 0,0 (entièrement relâché) et 1,0 (entièrement appuyé) représentées sous forme de valeurs à virgule flottante. La valeur du contrôle d’accélération est lue à partir de la propriété Throttle de la structure RacingWheelReading ; la valeur du contrôle de frein est lue à partir de la propriété Brake.

float throttle = reading.Throttle;  // returns a value between 0.0 and 1.0
float brake    = reading.Brake;     // returns a value between 0.0 and 1.0

Lecture du frein à main et de l’embrayage

Le frein à main et l’embrayage sont des contrôles facultatifs qui fournissent chacun des lectures analogiques comprises entre 0,0 (entièrement relâché) et 1,0 (entièrement appuyé) représentées sous forme de valeurs à virgule flottante. La valeur du contrôle de frein à main est lue à partir de la propriété Handbrake de la structure RacingWheelReading ; la valeur du contrôle d’embrayage est lue à partir de la propriété Clutch.

float handbrake = 0.0;
float clutch = 0.0;

if(racingwheel->HasHandbrake)
{
    handbrake = reading.Handbrake;  // returns a value between 0.0 and 1.0
}

if(racingwheel->HasClutch)
{
    clutch = reading.Clutch;        // returns a value between 0.0 and 1.0
}

Lecture du levier de vitesse

Le levier de vitesse est un contrôle facultatif qui fournit une lecture numérique entre -1 et MaxPatternShifterGear représentée sous la forme d’une valeur entière signée. Une valeur de -1 ou 0 correspond à la marche arrière et au point mort, respectivement ; les valeurs positives incrémentielles correspondent aux vitesses de marche avant jusqu’à MaxPatternShifterGear compris. La valeur du levier de vitesse est lue à partir de la propriété PatternShifterGear de la structure RacingWheelReading.

if (racingwheel->HasPatternShifter)
{
    gear = reading.PatternShifterGear;
}

Remarque

Le levier de vitesse, où il est pris en charge, existe en même temps que les boutons Vitesse inférieure et Vitesse supérieure requis qui affectent également le rapport de vitesse actuel de la voiture du joueur. Pour unifier ces entrées lorsque les deux éléments sont présents, vous pouvez tout simplement ignorer le levier de vitesses (et l’embrayage) lorsqu’un joueur choisit une voiture avec une transmission automatique, et ignorer les boutons Vitesse inférieure et Vitesse supérieure lorsqu’il opte pour une transmission manuelle, uniquement si le volant de course est doté d’un contrôle de levier de vitesse. Vous pouvez implémenter une stratégie d’unification différente si celle-ci n’est pas adaptée à votre jeu.

Exécuter l’exemple InputInterfacing

L’exemple d’application InputInterfacingUWP sur GitHub montre comment utiliser des volants de course et différents types de périphériques d’entrée en tandem, ainsi que la façon dont ces périphériques d’entrée se comportent en tant que contrôleurs de navigation de l’interface utilisateur.

Vue d’ensemble du retour de force

De nombreux volants de course ont une capacité de retour de force pour fournir une expérience de conduite plus immersive et difficile. Les volants de course qui prennent en charge les retours de force sont généralement équipés d’un moteur unique qui applique la force au volant de direction le long d’un axe unique, l’axe de rotation du volant. Le retour de force est pris en charge dans Windows 10 ou Windows 11 et les applications UWP Xbox One via l’espace de noms Windows.Gaming.Input.ForceFeedback.

Remarque

Les API de retour de force peuvent gérer plusieurs axes de force, mais aucun volant de course ne gère actuellement un axe de retour autre que celui de la rotation du volant.

Utilisation du retour de force

Ces sections décrivent les principes fondamentaux de la programmation des effets de retour de force pour les volants de course. Le retour est appliqué à l’aide d’effets, qui sont d’abord chargés sur l’appareil de retour de force, puis qui peuvent être démarrés, suspendus, repris et arrêtés d’une manière similaire aux effets sonores ; toutefois, vous devez d’abord déterminer les capacités de retour du volant de course.

Détermination des fonctionnalités de retour de force

Vous pouvez déterminer si un volant de course connecté prend en charge le retour de force en lisant la propriété WheelMotor du volant de course. Si la propriété WheelMotor a la valeur null, cela signifie que le retour de force n’est pas pris en charge ; toute autre valeur indique qu’il est géré et vous pouvez déterminer les fonctionnalités de retour spécifiques du moteur, par exemple les axes susceptibles d’être affectés.

if (racingwheel->WheelMotor != nullptr)
{
    auto axes = racingwheel->WheelMotor->SupportedAxes;

    if(ForceFeedbackEffectAxes::X == (axes & ForceFeedbackEffectAxes::X))
    {
        // Force can be applied through the X axis
    }

    if(ForceFeedbackEffectAxes::Y == (axes & ForceFeedbackEffectAxes::Y))
    {
        // Force can be applied through the Y axis
    }

    if(ForceFeedbackEffectAxes::Z == (axes & ForceFeedbackEffectAxes::Z))
    {
        // Force can be applied through the Z axis
    }
}

Chargement des effets de retour de force

Les effets de retour de force sont chargés sur l’appareil de retour où ils sont « lus » de manière autonome à la commande de votre jeu. Nous fournissons certains effets de base, mais vous pouvez créer des effets personnalisés via une classe qui implémente l’interface IForceFeedbackEffect.

Classe Effect Description de l’effet
ConditionForceEffect Effet qui applique une force variable en réponse au capteur actuel au sein de l’appareil.
ConstantForceEffect Effet qui applique une force constante le long d’un vecteur.
PeriodicForceEffect Effet qui applique une force variable définie par une forme d’onde, le long d’un vecteur.
RampForceEffect Effet qui applique une force linéairement croissante/décroissante le long d’un vecteur.
using FFLoadEffectResult = ForceFeedback::ForceFeedbackLoadEffectResult;

auto effect = ref new Windows.Gaming::Input::ForceFeedback::ConstantForceEffect();
auto time = TimeSpan(10000);

effect->SetParameters(Windows::Foundation::Numerics::float3(1.0f, 0.0f, 0.0f), time);

// Here, we assume 'racingwheel' is valid and supports force feedback

IAsyncOperation<FFLoadEffectResult>^ request
    = racingwheel->WheelMotor->LoadEffectAsync(effect);

auto loadEffectTask = Concurrency::create_task(request);

loadEffectTask.then([this](FFLoadEffectResult result)
{
    if (FFLoadEffectResult::Succeeded == result)
    {
        // effect successfully loaded
    }
    else
    {
        // effect failed to load
    }
}).wait();

Utilisation des effets de retour de force

Une fois chargés, les effets peuvent tous être démarrés, suspendus, repris et arrêtés de manière synchrone en appelant des fonctions sur la propriété WheelMotor du volant de course, ou individuellement en appelant des fonctions sur l’effet de retour lui-même. En règle générale, vous devez charger tous les effets que vous souhaitez utiliser sur l’appareil de retour avant le début du jeu, puis utiliser leurs fonctions SetParameters respectives pour mettre à jour les effets à mesure que le jeu progresse.

if (ForceFeedbackEffectState::Running == effect->State)
{
    effect->Stop();
}
else
{
    effect->Start();
}

Enfin, vous pouvez activer, désactiver ou réinitialiser de manière asynchrone l’ensemble du système de retour de force sur un volant de course particulier chaque fois que vous en avez besoin.

Voir aussi