Leçon 4 : Commandes
Maintenant que vous maîtrisez la couche graphique de SDL, nous allons nous attaquer à la couche dédiée à la gestion et à l'interprétation des commandes.
Clavier
Nous allons commencer par nous occuper du clavier. Tout d'abord, nous allons l'initialiser, puis tester l'état de différents touches (pressées ou non...) et enfin nous lancer dans la création d'une architecture de gestion des commandes (une première en C et une seconde en C++). Mais démarrons sans plus attendre :
#include <SDL/SDL.h> #include <stdio.h> #include <stdlib.h> // Objet de gestion SDL_Event event; // Initialisation de SDL_Event void inputInit(void) { // Traduction UNICODE SDL_EnableUNICODE(1); // Répétition des touches SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL + 50); }
Ce qui est suffisant ! En effet, SDL_Event ne recquiert pas d'initialisation précise, il suffit de créer un objet de type SDL_Event, la suite ne concernant que l'activation de diverses fonctionnalités. La traduction UNICODE permet de guarantir la compatibilité entre les machines où vous utiliserez le programme, et la répétition des touches est un plus très utilisé dans les jeux vidéos. Nous avons ici la partie commande de SDL parfaitement initialisée et prête à être utilisée. Oui, mais comment s'en servir ?
void input() { // Traitement des commandes while (SDL_PollEvent(&event)) { // Type d'évènement switch (event.type) { case SDL_KEYDOWN: // Si on presse une touche switch (event.key.keysym.sym) { case SDLK_ESCAPE: // Actions pour ECHAP break; case SDLK_LEFT: // Actions pour <- break; case SDLK_RETURN: // Actions pour ENTREE break; case SDLK_w: /* touche Z */ break; case SDLK_s: /* touche S */ break; case SDLK_a: /* touche Q */ break; case SDLK_d: /* touche D */ break; default: break; } break; // Si on relache une touche case SDL_KEYUP: break; // Fermeture du programme case SDL_QUIT: exit(0); default: break; } } }
Explications : à partir de l'objet SDL_Event et de la fonction SDL_PollEvent() associée, on à accès aux diverses informations dont on a besoin (quelles touches sont pressées, mouvements de la souris...) Ici, tant que la fonction SDL_PollEvent lit les évènements dans l'objet event (déclaré plus haut), on teste le type d'évènement (état des touches...) par un switch sur l'objet event.type. Si le type est SDL_KEYDOWN, l'utilisateur a pressé une touche : dans ce cas là, on la teste (objet event.key.keysym.sym) et si elle correspond à une des touches définies (SDLK_NOM_TOUCHE) on effectue les opérations pour cette touche. Par exemple, pour savoir si l'utilisateur presse la touche [ECHAP], on teste la valeur SDLK_ESCAPE dans le switch sur event.key.keysym.sym : si oui, on effectue les actions pour [ECHAP] avant le break.
Avancé: en réalité, l'objet SDL_Event est une file d'évènement, c'est à dire un objet qui emmagasine à la suite et dans l'ordre chronologique tous les évènements venant de l'utilisateur. Quand on utilise la fonction SDL_PollEvent(), celle-ci lit le premier élément de la file d'attente et l'affecte à l'objet (après quoi on peut le tester). A noter qu'à la place de la fonction SDL_PollEvent, qui vide la file une fois lue, on peut utiliser la fonction SDL_WaitEvent(&event);, qui "fige" le programme jusqu'à ce que l'utilisateur agisse sur les commandes. Vous pouvez aussi "jeter un coup d'oeil" aux évènements de la file d'évènements sans les enlever en passant SDL_PEEKEVENT comme paramètre à la fonction SDL_PeepEvents().
A noter qu'au lieu de savoir si l'utilisateur presse une touche, on peut savoir s'il relâche une touche (SDL_KEYUP), s'il tente de fermer le programme (SDL_QUIT) ou s'il bouge la souris (voir plus bas). Comme vous pouvez le voir, gérer le clavier avec SDL est vraiment simple : en deux fonctions on arrive déjà à déterminer les actions de l'utilisateur sur le clavier. Nous allons désormais nous intéresser à d'autres périphériques : la souris et les manettes !
Souris
Nous allons maintenant nous intéresser à la souris. Pour gérer la souris dans un programme, on a besoin de connaître la position du pointeur en X et en Y à l'écran, ainsi que les infos d'état sur la souris (boutons enfoncés, molette...) Là encore, SDL nous mâche le travail, et il suffit de reprendre le switch ci-dessus (celui sur event.type) et y ajouter les cas suivants :
// Variables int iMouseX, iMouseY; /* [...] Code de l'exemple précédent */ case SDL_MOUSEBUTTONDOWN: // Actions à éfféctuer lors d'un clic if ((iMouseX < 32) && (iMouseY < 32)) { // Clic en haut à gauche } break; case SDL_MOUSEMOTION: // Actions à éffectuer lors d'un mouvement de la souris getMousePosition(); break; /* Suite du code précédent [...] */
Nous testons ici les deux conditions classiques : le clic et le mouvement de la souris. En cas de clic (gauche ou droit, pour définir quel bouton de la souris est solicité vous devrez utiliser les constantes type SDL_BUTTON_LEFT avec un code style if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_LEFT)); qui teste si le clic effectué est un clic gauche ou non). Ici, quel que soit le clic, on teste sa position : si l'utilisateur clique en haut à gauche de l'écran (marge de 32px), on effectue les actions entre crochets (laissez libre cours à votre imagination, je n'étais pas inspiré . Mais, pour que les variables iMouseX et iMouseY soient à jour, on effectue à chaque mouvement de la souris un appel à la fonction getMousePosition(void) pour les mettre à jour. Quelle est cette fonction ? Bien, une petite fonction maison :
// Affecter la position du pointeur void testMousePosition(void) { // Récupérer la liste d'évènements SDL_PumpEvents(); // Solution 1 SDL_GetMouseState(&iMouseX, &iMouseY); // Solution 2 iMouseX = event.motion.x; iMouseY = event.motion.y; return; }
J'ai mis ici deux solutions, prenez celle qui vous convient le mieux. Une fois cette fonction appelée, la position de la souris est à jour. A partir de ça, vous êtes capable d'obtenir toutes les informations que vous voulez sur la souris et de l'utiliser dans vos jeux. Vous pouvez cacher ou montrer le curseur de votre souris en utilisant SDL_ShowCursor(bool show) : si show est mis à true, la curseur est affiché, sinon... non! Bon, désormais vous commencez à en connaître un rayon sur SDL et les commandes, il ne vous reste plus qu'un point à explorer, et pas des moindres pour un créateur de jeux : les manettes (et autres joysticks ...
Note sur les évènements
Afin de manipuler directement les évènements, chaque type d'évènement à une fonction qui vous permet de contrôler l'état du programme. Si vous n'utilisez que ce système, vous pouvez ignorer tous les évènements grâce à SDL_EventState() et appeler régulièrement SDL_PumpEvents() pour mettre à jour l'état de l'application.
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
Manettes et joysticks
Pour pouvoir utiliser les joysticks il vous faudra initialiser SDL en utilisant le flag SDL_INIT_JOYSTICKS (SDL_Init(SDL_INIT_JOYSTICK);) Afin d'utiliser les joysticks, vous devrez les lister et en choisir un. La fonction SDL_NumJoysticks() renvoie le nombre de joysticks connectés à la machine, et afin que l'utilisateur puisse en choisir un on peut utiliser le code suivant :
printf("%d joysticks connectés :\n\n", SDL_NumJoysticks()); for (unsigned char i = 0; i < SDL_NumJoysticks(); i++) printf("%d/ %s\n", i, SDL_JoystickName(i)); printf("Utiliser le joystick :"); int iJoystick = getch();
Après quoi on saura que le Joystick utilisé est le joystick numéro iJoystick. Pour utiliser des joysticks vous devrez tout d'abord activr la récupération des évènements Joystick grâce à la fonction SDL_JoystickEventState(), puis utiliser un objet SDL_Joystick* initialisé grâce à la fonction SDL_JoystickOpen(int num) où num correspond à iJoystick. Voici un exemple de code qui fonctionne :
// Objet SDL_Joystick SDL_Joystick* Joystick; // Initialisation du Joystick SDL_JoystickEventState(SDL_ENABLE); Joystick = SDL_JoystickOpen(iJoystick);
N'oubliez surtout pas de fermer l'objet à la fin du programme grâce à la fonction suivante : SDL_JoystickClose(Joystick);.
Le manche
Dès lors, il ne nous reste plus qu'à reprendre la boucle d'écoute des évènements vue plus haut pour le clavier et la souris et à rajouter au switch sur event.type le code suivant :
// Mouvement du Joystick case SDL_JOYAXISMOTION: if (event.jaxis.value < -3200 || event.jaxis.value > 3200 ) { if(event.jaxis.axis == 0) { // Mouvements gauche/droite } else if( event.jaxis.axis == 1) { // Mouvements haut/bas } } break;
Où event.jaxis.value correspond à la valeur d'inclinaison du manche, ici assez élevée : vous pourrez jouer la dessus pour déterminer certains paramètres (inclinaison d'un vaisseau...) plus ou moins sensibles. Ici, on teste les mouvements du joystick, s'il y a mouvement on teste l'inclinaison et si elle est assez importante on exécute le code pour des mouvements horizontaux du manche (event.jaxis.axis=0) ou verticaux (event.jaxis.axis=1). A partir de ce code vous êtes en mesure d'utiliser le manche du joystick dans vos programmes, restent les boutons et un éventuel "chapeau" du joystick.
Les boutons
Tester les boutons est encore plus simple que de tester les axes du joystick, il suffit d'utiliser les constantes SDL_JOYBUTTONDOWN ou SDL_JOYBUTTONUP, toujours au sein de notre merveilleux switch ;) En effet, ceci fait il nous suffit d'accèder au bouton n en testant si event.jbutton.button est égal à n, comme l'illustre le code suivant.
// Boutons du Joystick case SDL_JOYBUTTONDOWN: if (event.jbutton.button == 0) { // Code pour le bouton 0 } break;
Le chapeau
Je ne parlerai pas ici des "Joyballs" des Joysticks mais je vais aborder les "Chapeaux", ses petits joysticks sur le manche qui ne peuvent être dirigés que dans une seule direction à la fois. On peut connaître cette direction en utilisant les constantes suivantes :
- SDL_HAT_CENTERED
- SDL_HAT_UP
- SDL_HAT_RIGHT
- SDL_HAT_DOWN
- SDL_HAT_LEFT
- SDL_HAT_RIGHTUP
- SDL_HAT_RIGHTDOWN
- SDL_HAT_LEFTUP
- SDL_HAT_LEFTDOWN
On peut donc inclure, toujours dans ce formidable switch qui nous sert depuis le début du cours, le code suivant :
// Mouvement du chapeau case SDL_JOYHATMOTION: if (event.jhat.hat & SDL_HAT_UP) { // Vers le haut } else if (event.jhat.hat & SDL_HAT_RIGHTDOWN) { // Vers la diagonale droit-bas } break;
Conclusion
Au cours de ce cours, vous avez appris à utiliser et à maîtriser différents périphériques systèmes pour recevoir les "ordres" de l'utilisateur et les intérprêter dans vos programmes. La librairie SDL vous permet aussi de connaître les caractéristiques du matériel, surtout pour les joysticks, grâce aux fonctions (pour les joysticks) suivantes : SDL_JoystickNumAxes , SDL_JoystickNumButtons, SDL_JoystickNumBalls, SDL_JoystickNumHats... qui retournent des int et prennent l'élément SDL_Joystick comme argument.
Voici quelques projets open-source qui mettent en pratique les notions de ce cours :