Aller au menu - Aller au contenu

[Plan du site] Vous êtes ici --- > Le Site du Zéro > Les tutoriels > Officiels > Programmation > Apprenez à programmer en C ! > [Pratique] Création de jeux 2D en SDL > La gestion des évènements (Partie 2/2) > Lecture du tutoriel

La gestion des évènements (Partie 2/2)

Avatar
Auteur : M@teo21
Difficulté : Connaisseur (3 / 5)
Note : 19 / 20 (9 votes)
Visualisations : 105 460

Plus d'informations Plus d'informations
Nous n'avons pas encore vu tous les évènements !
Comme il existe de nombreux évènements différents, je ne pouvais pas tout mettre dans un même chapitre. J'ai donc préféré le couper en 2 :)

Dans ce chapitre, nous allons étudier les évènements :



La gestion du joystick est un peu délicate vous allez voir. Toutefois, comme la SDL nous permet de manier le joystick quel que soit le système d'exploitation (Windows, Mac, Linux)... je ne pouvais pas passer à côté !

Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Initialiser le joystick

Image utilisateur
En plus du clavier et de la souris, qui sont des outils de contrôle courants, la SDL gère aussi... le joystick !

Vous pensez peut-être que le joystick est plus difficile à gérer parce qu'il y a des boutons, un manche qu'on peut plus ou moins incliner etc... Eh bien, la manipulation du joystick nécessite quelques initialisations supplémentaires c'est vrai, mais à part ça c'est aussi facile à gérer que le clavier et la souris alors ne nous en privons pas :)
Grâce à ce que vous allez apprendre, vous serez capables de créer un jeu qu'on peut manipuler au clavier, à la souris ou au joystick (ou avec les 3 à la fois :lol: )


Initialiser le joystick



Si vous comptez utiliser un joystick dans votre programme, vous devrez effectuer quelques initialisations supplémentaires. Le clavier et la souris, eux, n'ont pas besoin d'être initialisés comme vous l'avez vu.


Initialisation du système de joystick de la SDL



Pour commencer, vous devez indiquer à SDL_Init que vous comptez utiliser le joystick dans votre programme.

Jusqu'ici, vous ne devriez avoir envoyé qu'un seul flag à cette fonction :

Code : C
1
SDL_Init(SDL_INIT_VIDEO);


Tout ce que vous devez faire, c'est rajouter le flag SDL_INIT_JOYSTICK que vous combinez au précédent à l'aide du symbole "|" :

Code : C
1
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);


Voilà, la SDL sait maintenant qu'elle va devoir gérer les joysticks :)


Compter le nombre de joysticks



Contrairement au clavier et la souris qu'on ne branche pas en double sur un ordinateur, il se peut que vous ayez plusieurs joysticks branchés (que ce soit sur le port de jeu ou sur un port USB).

On va compter le nombre de joysticks installés sur votre système. Pour ce faire, il suffit d'appeler SDL_NumJoysticks() qui renvoie le nombre de joysticks. Cette fonction ne prend aucun paramètre.

Le problème, ça va être pour afficher la valeur. En SDL, il n'y a pas de fonction pour afficher du texte dans la fenêtre. Heureusement, nous apprendrons dans quelques chapitres qu'à l'aide d'une librairie supplémentaire (dans le même genre que SDL_Image) il sera possible d'afficher du texte dans une fenêtre SDL.
Mais... pour le moment, on n'en est pas là ^^

On va utiliser la bonne vieille fonction printf :

Code : C
1
printf("Il y a %ld joysticks connectés\n", SDL_NumJoysticks());


Mais... s'il n'y a plus de console à l'écran où va s'afficher ce texte ?



Sous Windows vous devrez donc ouvrir le fichier stdout.txt qui sera automatiquement créé par le système d'exploitation lorsque vous utiliserez printf. Ce fichier reste sur le disque dur même après exécution du programme. Vous pourrez le supprimer manuellement sans crainte, il sera simplement recréé lors du prochain démarrage du programme.


Voici ce que printf a écrit chez moi :

Code : Console
Il y a 1 joysticks connectés


Cela signifie (je pense que vous l'aurez compris :D ) que j'ai 1 joystick branché sur mon ordinateur.
Si le joueur a 0 joystick, il ne pourra pas manipuler votre programme au joystick (ça peut sembler logique :-° ) il faudra donc le rabattre sur une manipulation plus classique au clavier et à la souris.


Lister les noms des joysticks



La SDL nous permet d'avoir le nom des joysticks installés sur l'ordinateur. Cela devrait permettre de laisser au joueur le choix du joystick qu'il veut utiliser.

Je ne connais pas beaucoup de gens qui aient plusieurs joysticks branchés sur leur ordinateur, mais la SDL prévoit qu'il puisse y en avoir plusieurs ce qui n'est pas plus mal :)


Les joysticks sont numérotés de 0 à... autant qu'il y a de joysticks :p
Si vous n'avez qu'un joystick, ce sera donc le n°0.

On récupère le nom du joystick n°X en appelant : SDL_JoystickName(X). Cette fonction prend donc un paramètre : le numéro du joystick dont on veut le nom.
Puisqu'on y est, on va faire une boucle pour lister tous les joysticks présents sur l'ordinateur :

Code : C
1
2
3
4
for (i = 0 ; i < SDL_NumJoysticks() ; i++ ) 
{
    printf("Joystick n°%ld : %s\n", i, SDL_JoystickName(i));
}


Voici le résultat que ça donne chez moi :

Code : Console
Il y a 1 joysticks connectés

-> Joystick n°0 : Boîtier de commandes Microsoft SideWinder


Comme vous le voyez, je possède un joystick appelé "Boîtier de commandes Microsoft SideWinder".


Charger le joystick désiré



Bien, maintenant on va dire à la SDL le numéro du joystick qu'on veut utiliser dans notre programme.
Dans mon cas ça ne sera pas très compliqué, je vais utiliser mon Joystick n°0 :p

Si vous ne savez pas quel numéro de joystick choisir, prenez le n°0 (à condition qu'il y ait au moins un joystick sur l'ordinateur).
Plus tard, lorsque vous serez plus à l'aise avec la SDL, vous serez capables de demander au joueur le joystick qu'il veut utiliser.


On va avoir besoin de créer un pointeur de type SDL_Joystick pour manipuler le joystick. Vous pouvez donc rajouter la ligne suivante au début du main :

Code : C
1
SDL_Joystick *joystick = NULL;


Ensuite, vous allez devoir appeler 2 fonctions :

Code : C
1
2
SDL_JoystickEventState(SDL_ENABLE);
joystick = SDL_JoystickOpen(0);


La première active la gestion des évènements des joysticks.
La seconde "ouvre" (= charge) le joystick n°0, c'est-à-dire le premier joystick. Le résultat doit être stocké dans une variable joystick de type SDL_Joystick.

A la fin de votre programme (avant SDL_Quit), vous devrez "fermer" le joystick en appelant :

Code : C
1
SDL_JoystickClose(joystick);

Les évènements du joystick

Les propriétés du joystick



Nous avons vu comment charger un joystick.
Maintenant que c'est fait, essayons de récupérer un maximum d'infos sur ce joystick ouvert.

Il faut savoir qu'un joystick peut avoir 4 propriétés différentes, chacune pouvant générer des évènements :



Je possède personnellement un joystick assez simple. C'est un Sidewinder de Microsoft, comme je vous l'ai dit précédemment :

Image utilisateur


Sur mon joystick, il n'y a que des boutons et un stick directionnel (donc des "axes"). Il n'y a ni trackballs ni chapeaux. Je doute d'ailleurs fortement qu'il existe un joystick avec tous ces éléments à la fois (ou alors c'est une vraie machine de guerre o_O ).

Bref, tout ça pour dire que je ne pourrai vous présenter que les évènements "boutons" et "axes". Heureusement, ce sont les plus courants et vous pourrez déjà tout faire avec ça. Le jour où vous aurez besoin de gérer des trackballs ou des chapeaux (ce qui n'est pas courant), vous serez déjà loin et bien meilleur que moi :p


Lister le nombre de boutons, d'axes etc...



Il y a 4 fonctions pour compter le nombre de chacun de ces éléments (boutons, axes, trackballs et chapeaux) présents sur le joystick.
Vous pouvez tester avec ce code :

Code : C
1
2
3
4
printf("----> %d boutons\n", SDL_JoystickNumButtons(joystick));
printf("----> %d axes\n", SDL_JoystickNumAxes(joystick));
printf("----> %d trackballs\n", SDL_JoystickNumBalls(joystick));
printf("----> %d chapeaux\n", SDL_JoystickNumHats(joystick));


Attention, le joystick doit avoir été ouvert au préalable. En effet, comme vous le voyez il faut envoyer à ces fonctions notre pointeur joystick.

Le résultat donné pour mon Sidewinder est le suivant :

Code : Console
----> 10 boutons

----> 2 axes

----> 0 trackballs

----> 0 chapeaux


Je possède donc 10 boutons et 2 axes : un axe de haut en bas, un autre axe de gauche à droite.
Je ne possède aucun trackball et aucun chapeau, comme prévu.


Les évènements du joystick



Comme je vous l'ai dit, un joystick peut générer de nombreux évènements parce qu'il y a plusieurs types de joysticks. Je vais parler des évènements les plus courants ici : les boutons et les axes.


Les boutons



Tout d'abord, commençons par un évènement commun à tous les joysticks : l'appui sur un bouton !

Vous avez 2 évènements possibles :


Bref, classique :)

On récupère le numéro du bouton enfoncé à l'aide de la variable : event.jbutton.button :

Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
switch(event.type)
{
    case SDL_QUIT:
        continuer = 0;
        break;
    case SDL_JOYBUTTONDOWN:
        if (event.jbutton.button == 0) /* Arrêt du programme si on appuie sur le 1er bouton */
            continuer = 0;
        break;
}


Ici, le programme devrait s'arrêter si on appuie sur le bouton n°0 (le premier bouton :) )


Le stick directionnel (axe)



Un déplacement de l'axe du joystick génère un évènement de type SDL_JOYAXISMOTION.

Les évènements liés aux "axes" peuvent être générés soit par un stick directionnel (comme c'est le cas sur mon Sidewinder), soit par un manche comme c'est le cas sur les joysticks un peu plus chers que l'on utilise pour les jeux d'avion.

Quelle est la différence concrètement entre un stick directionnel et un manche ?


On peut contrôler plus finement l'inclinaison d'un manche que celle d'un stick directionnel. Un manche est donc parfait pour un jeu d'avion. En revanche, le stick directionnel est toujours poussé "à fond", on ne peut pas contrôler son inclinaison.

Si je vous dis tout ça, c'est parce que la SDL nous renvoie l'inclinaison du manche. On la récupère dans event.jaxis.value. C'est un nombre négatif si on se déplace vers la gauche ou vers le haut, et un nombre positif si on se déplace vers la droite ou vers le bas.

En général, on teste l'inclinaison comme ceci :

Code : C
1
2
3
4
5
6
case SDL_JOYAXISMOTION:
    if (event.jaxis.value < -3200 || event.jaxis.value > 3200)
    {
        /* Le manche (ou le stick) a été déplacé de sa position initiale */
    }
    break;


Si on rentre dans le if, c'est que le manche a été poussé (mais on ne sait pas encore dans quelle direction).
En temps normal, le manche est en position centrale et event.jaxis.value vaut 0.

Sur mon Sidewinder, comme il n'y a qu'un stick directionnel, des valeurs extrêmes sont générées pour value : -32768 et 32767 (selon le sens dans lequel on se dirige). Si vous avez un manche, vous pouvez utiliser value pour gérer plus finement le déplacement (comme l'inclinaison d'un vaisseau).

Bon, maintenant qu'on sait que le manche a été un minimum déplacé, on veut connaître la direction.
Pour ça, on doit regarder la variable event.jaxis.axis. Elle vaut :



En combinant intelligemment event.jaxis.axis et event.jaxis.value, on peut donc savoir dans quelle direction on veut se déplacer :

Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
case SDL_JOYAXISMOTION:
    if (event.jaxis.axis == 0 && event.jaxis.value < -3200) /* Vers la gauche */
        positionZozor.x--;
    else if (event.jaxis.axis == 0 && event.jaxis.value > 3200) /* Vers la droite */
        positionZozor.x++;
    else if (event.jaxis.axis == 1 && event.jaxis.value < -3200) /* Vers le haut */
        positionZozor.y--;
    else if (event.jaxis.axis == 1 && event.jaxis.value > 3200) /* Vers le bas */
        positionZozor.y++;
    break;



Et voilà le travail ! :soleil:

Les évènements de la fenêtre

Il ne nous reste plus qu'à voir les évènements liés à la fenêtre et nous aurons vu pratiquement tous les évènements de la SDL !
Je sais, ça demande du travail, mais c'est vraiment la base de tout programme écrit en SDL, donc autant mettre le paquet ;)

Quels évènements peuvent être générés par la fenêtre ?




Y'a de quoi faire !
Commençons par l'évènement généré lors du redimensionnement de la fenêtre.


Le redimensionnement de la fenêtre



Par défaut, une fenêtre SDL ne peut pas être redimensionnée par l'utilisateur.
Je vous rappelle que pour changer ça, il faut ajouter le flag SDL_RESIZABLE dans la fonction SDL_SetVideoMode :)

Code : C
1
ecran = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_RESIZABLE);



Lorsque l'utilisateur redimensionne la fenêtre, un évènement SDL_VIDEORESIZE est généré.

Vous pouvez récupérer :



Code : C
1
2
3
4
case SDL_VIDEORESIZE:
    positionZozor.x = event.resize.w / 2 - zozor->w / 2;
    positionZozor.y = event.resize.h / 2 - zozor->h / 2;
    break;


Avec ce code, Zozor sera toujours centré dans la fenêtre quelle que soit sa taille :)


Visibilité de la fenêtre



L'évènement SDL_ACTIVEEVENT est généré lorsque la visibilité de la fenêtre change.
Cela peut être dû à de nombreuses choses :


En programmation, on parle de focus.
Lorsqu'on dit qu'une application a le focus, c'est que le clavier ou la souris de l'utilisateur se trouve dedans. Tous les clics ou appuis des touches du clavier que vous ferez seront envoyés à la fenêtre qui a la focus et pas aux autres.
Une seule fenêtre peut avoir le focus à la fois (vous ne pouvez pas avoir 2 fenêtres au premier plan en même temps !).


Vu que plusieurs choses peuvent s'être passées, il faut regarder dans des variables pour en savoir plus :


Vous suivez toujours ? ^^
Il faut donc bien comparer les valeurs des 2 variables pour savoir exactement ce qui s'est produit.


Tester la valeur d'une combinaison de flags



Le seul problème, c'est que event.active.state est une combinaison de flags. Cela signifie que dans un évènement il peut se produire 2 choses à la fois (par exemple si on réduit la fenêtre dans la barre des tâches, on perd aussi le focus du clavier et de la souris).
Il va donc falloir faire un test un peu plus compliqué que (par exemple) : if (event.active.state == SDL_APPACTIVE)

Pourquoi est-ce que c'est plus compliqué ?


Parce que c'est une combinaison de bits. Je ne vais pas vous faire un cours sur les opérations logiques bit à bit ici, ça serait un peu trop pour le cours et vous n'avez pas besoin de connaître le détail.
Je vais vous donner le code qu'il faut utiliser pour tester si un flag est présent dans une variable sans rentrer dans les détails ;)

Pour tester s'il y a eu un changement de focus de la souris, on doit taper le code suivant :

Code : C
1
if ((event.active.state & SDL_APPMOUSEFOCUS) == SDL_APPMOUSEFOCUS)


Il n'y a pas d'erreur. Attention c'est précis : il faut un seul & et deux =, et il faut bien mettre les parenthèses comme je l'ai fait ;)

Ca marche de la même manière pour les autres évènements. Par exemple :

Code : C
1
if ((event.active.state & SDL_APPACTIVE) == SDL_APPACTIVE)



Tester l'état et le gain à la fois



Dans la pratique, vous voudrez sûrement tester l'état et le gain à la fois. Vous pourrez ainsi savoir exactement ce qui s'est passé.

Supposons que vous ayez un jeu qui fait faire beaucoup de calculs à l'ordinateur. Vous voulez que le jeu se mette en pause automatiquement lorsque la fenêtre est réduite et qu'il se relance lorsque la fenêtre est réagrandie. Cela évite que le jeu ne continue pendant que le joueur n'est plus dessus, et cela évite aussi au processeur de faire trop de calculs par la même occasion.

Le code ci-dessous met en pause le jeu en activant par exemple un booléen pause à 1. Il remet en marche le jeu en désactivant le booléen à 0.

Code : C
1
2
3
4
5
6
7
if ((event.active.state & SDL_APPACTIVE) == SDL_APPACTIVE)
{
    if (event.active.gain == 0) /* La fenêtre a été réduite en barre des tâches */
        pause = 1;
    else if (event.active.gain == 1) /* La fenêtre a été restaurée */
        pause = 0;
}


Bien sûr ce code n'est pas complet. Ce sera à vous d'écrire le code pour tester l'état de la variable pause pour savoir s'il faut effectuer les calculs ou pas.


Ce qui compte, c'est que vous compreniez l'idée générale :)

Je vous laisse faire d'autres tests pour par exemple vérifier si la souris est à l'intérieur ou à l'extérieur de la fenêtre. Vous n'avez qu'à faire bouger Zozor vers la droite lorsque la souris rentre dans la fenêtre et le faire bouger vers la gauche lorsqu'elle en sort ;)

Q.C.M.

Faut-il avoir chargé un joystick pour en connaître les caractéristiques (nombre de boutons, d'axes) ?
Que représente la variable event.jaxis.axis ?
Quel évènement est généré lorsque la souris rentre sort de la fenêtre ?
Quelle est la bonne façon de tester si le flag SDL_APPINPUTFOCUS se trouve dans la variable event.active.state ?
Si je reçois un évènement SDL_APPMOUSEFOCUS et que j'ai un gain de 1, qu'est-ce que ça signifie ?

Statistiques de réponses au QCM


Vous comprenez un peu mieux maintenant pourquoi j'ai tenu à séparer le chapitre sur les évènements en 2. Il y avait vraiment beaucoup à dire !

En gros, on peut résumer les 2 chapitres comme ceci :



Personnellement je n'ai jamais créé de jeu utilisant le joystick à l'heure actuelle.
Enfin bon, tout est faisable comme vous l'avez vu ;)

Le chapitre suivant sera un TP. Ce TP vous montrera la création d'un jeu complet (Mario Sokoban) de A à Z en SDL.
Nous n'utiliserons pas le joystick ni les évènements de la fenêtre que nous avons étudiés dans ce chapitre. Toutefois, si vous désirez ajouter la possibilité d'utiliser le joystick dans le jeu, n'hésitez pas ! Ca fera un bon entraînement ! :)
Chapitre précédent Sommaire Chapitre suivant
Retour en haut Retour en haut


Créé : le 29/07/2005 à 00:29:36
Modifié : le 13/09/2008 à 17:04:21
Avancement : 100%
Licence : Copie non autorisée

11 commentaires

Changer de design | En savoir plus | Plan du site | Politique d'accessibilité | Règles | RSS tutoriels | RSS news
Édité par Simple IT SARL : Nous contacter | Notre blog | Revue de presse | Publicité

Y'a plus rien à lire, faut remonter maintenant !

Hébergement web - Correction de tutoriels - Créer un site
Vous souhaitez apparaître ici ? Contactez-nous.

Nombre de connectés 88 Zéros connectés | Requêtes SQL 8 requêtes | Temps de génération de la page : Total (SQL) 0.1048s (0.09s)