Manche à balai
Cet article explique les notions de base de la programmation pour les manches à balai certifiés Xbox One avec l’API Windows.Gaming.Input.FlightStick et les API associées pour la plateforme Windows universelle (UWP).
Voici ce que vous allez apprendre à la lecture de cet article :
- Obtenir une liste des manches à balai connectés et de leurs utilisateurs
- comment détecter qu’un manche à balai a été ajouté ou supprimé
- comment lire l’entrée à partir d’un ou plusieurs manches à balai
- comment les manches à balai se comportent en tant qu’appareils de navigation de l’interface utilisateur
Vue d’ensemble
Les manche à balai sont des périphériques d’entrée de jeu qui sont appréciés pour reproduire la sensation des manches à balai qui se trouveraient dans le cockpit d’un avion ou d’un vaisseau spatial. Ils sont les périphériques d’entrée parfaits pour un contrôle rapide et précis du vol. Les manches à balai sont pris en charge dans Windows 10 ou Windows 11 et les applications Xbox One via l’espace de noms Windows.Gaming.Input.
Les manches à balai certifiés Xbox One sont équipés des contrôles suivants :
- Une manette de jeu analogique maniable capable de roulis, de tangage et de lacet
- Un accélérateur analogique
- Deux boutons de tir
- Un bouton champignon numérique à 8 directions
- Les boutons Affichage et Menu
Remarque
Les boutons Affichage et Menu sont utilisés pour prendre en charge la navigation de l’interface utilisateur, et non les commandes de jeu, et ne sont donc pas facilement accessibles en tant que boutons de manette de jeu.
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 tant que contrôleur de navigation de l’interface utilisateur, un manche à balai mappe l’ensemble requis de commandes de navigation à la manette de jeu et aux boutons Affichage, Menu, TirPrincipal et TirSecondaire.
Commande de navigation | Entrée du manche à balai |
---|---|
Haut | Manette de jeu vers le haut |
Descendre | Manette de jeu vers le bas |
Left | Manette de jeu vers la gauche |
Right | Manette de jeu vers la droite |
Afficher | Bouton Afficher |
Menu | Bouton Menu |
Accepter | Bouton TirPrincipal |
Annuler | Bouton TirSecondaire |
Les manches à balai ne mappent aucune des commandes facultatives de navigation.
Détecter et suivre les manches à balai
La détection et le suivi des manche à balai fonctionnent exactement de la même façon que pour les boîtiers de commande, mais avec la classe FlightStick au lieu de la classe Gamepad. Pour plus d’informations, consultez Boîtier de commande et vibrations.
Lecture du manche à balai
Une fois que vous avez identifié le manche à balai qui vous intéresse, vous pouvez commencer à collecter les entrées de celui-ci. Toutefois, contrairement à d’autres sortes d’entrées que vous connaissez peut-être, les manches à balai ne communiquent pas les changements d’état en déclenchant des événements. À la place, vous devez interroger régulièrement ces manches à balai pour connaître leur état actuel.
Interrogation du manche à balai
Le processus d’interrogation capture un instantané du manche à balai à 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 manche à balai en appelant FlightStick.GetCurrentReading. Cette fonction retourne un FlightStickReading qui contient l’état du manche à balai.
L’exemple suivant interroge un manche à balai pour connaître son état actuel :
auto flightStick = myFlightSticks->GetAt(0);
FlightStickReading reading = flightStick->GetCurrentReading();
En plus de l’état du manche à balai, chaque lecture inclut un timestamp qui indique précisément quand l’état a été récupéré. Le timestamp est utile pour se rapporter au minutage des lectures précédentes ou au minutage de la simulation de jeu.
Lecture de l’entrée de la manette de jeu et de l’accélérateur
Le joystick fournit une lecture analogique comprise entre -1,0 et 1,0 dans les axes X, Y et Z (roulis, tangage et lacet, respectivement). Pour le roulis, une valeur de -1,0 correspond à la position de manette de jeu la plus à gauche, tandis qu’une valeur de 1,0 correspond à la position la plus à droite. Pour le tangage, la valeur -1,0 correspond à la position de manette de jeu la plus basse, tandis qu’une valeur de 1,0 correspond à la position la plus haute. Pour le lacet, une valeur de -1,0 correspond à la position de rotation anti-horaire maximale, tandis qu’une valeur de 1,0 correspond à la position de rotation horaire maximale.
Dans tous les axes, la valeur est d’environ 0,0 lorsque la manette de jeu est en position centrale, mais il est normal que la valeur précise varie, même entre les lectures suivantes. Les stratégies d’atténuation de cette variation sont décrites plus loin dans cette section.
La valeur de roulis de la manette de jeu est lue à partir de la propriété FlightStickReading.Roll, la valeur du tangage est lue à partir de la propriété FlightStickReading.Pitch et la valeur du lacet est lue à partir de la propriété FlightStickReading.Yaw :
// Each variable will contain a value between -1.0 and 1.0.
float roll = reading.Roll;
float pitch = reading.Pitch;
float yaw = reading.Yaw;
Lorsque vous lisez les valeurs de la manette de jeu, vous remarquerez qu’elles ne produisent pas de manière fiable une lecture neutre de 0,0 lorsque le joystick est au repos dans la position centrale. Au lieu de cela, elles produisent des valeurs différentes près de 0,0 chaque fois que le joystick est déplacé et retourné à la position centrale. Pour atténuer ces variations, vous pouvez implémenter une petite zone morte, qui est une plage de valeurs proche de la position centrale idéale qui sont ignorées.
Une façon d’implémenter une zone morte consiste à déterminer la distance à laquelle le joystick s’est déplacé du centre, et ignorer les lectures qui sont plus proches que la distance que vous choisissez. Les lectures de manette de jeu sont des valeurs essentiellement polaires, pas planaires. Vous pouvez calculer la distance approximative, non exacte, juste en utilisant le théorème de Pythagore. Cela produit une zone morte radiale.
L’exemple suivant illustre une zone morte radiale simple calculée à l’aide du théorème de Pythagore :
// Choose a deadzone. Readings inside this radius are ignored.
const float deadzoneRadius = 0.1f;
const float deadzoneSquared = deadzoneRadius * deadzoneRadius;
// Pythagorean theorem: For a right triangle, hypotenuse^2 = (opposite side)^2 + (adjacent side)^2
float oppositeSquared = pitch * pitch;
float adjacentSquared = roll * roll;
// Accept and process input if true; otherwise, reject and ignore it.
if ((oppositeSquared + adjacentSquared) < deadzoneSquared)
{
// Input accepted, process it.
}
Lecture des boutons et du bouton champignon
Chacun des deux boutons de tir du manche à balai fournit une lecture numérique qui indique s’il est appuyé (bas) ou relâché (haut). Pour plus d’efficacité, les entrées de bouton 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 FlightStickButtons. En outre, le bouton champignon à 8 directions fournit une direction empaquetée dans un champ de bits unique qui est représenté par l’énumération GameControllerSwitchPosition.
Remarque
Les manches à balai 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 font pas partie de l’énumération FlightStickButtons
et peuvent uniquement être lus en accédant au manche à balai en tant qu’appareil de navigation de l’interface utilisateur. Pour plus d’informations, consultez Contrôleur de navigation de l’interface utilisateur.
Les valeurs de bouton sont lues à partir de la propriété FlightStickReading.Buttons. 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 TirPrincipal est à l’état appuyé :
if (FlightStickButtons::FirePrimary == (reading.Buttons & FlightStickButtons::FirePrimary))
{
// FirePrimary is pressed.
}
L’exemple suivant détermine si le bouton TirPrincipal est à l’état relâché :
if (FlightStickButtons::None == (reading.Buttons & FlightStickButtons::FirePrimary))
{
// FirePrimary 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 chacune de ces conditions, consultez Détection des transitions de boutons et Détection des dispositions complexes des boutons.
La valeur du bouton champignon est lue à partir de la propriété FlightStickReading.HatSwitch. Étant donné que cette propriété est également un champ de bits, le masquage au niveau du bit est de nouveau utilisé pour isoler la position du bouton champignon.
L’exemple suivant détermine si le bouton champignon est en position haute :
if (GameControllerSwitchPosition::Up == (reading.HatSwitch & GameControllerSwitchPosition::Up))
{
// The hat switch is in the up position.
}
L’exemple suivant détermine si le bouton champignon est en position centrale :
if (GameControllerSwitchPosition::Center == (reading.HatSwitch & GameControllerSwitchPosition::Center))
{
// The hat switch is in the center position.
}