Big Tuto SDL 2 : Rabidja v. 3.0

Chapitre 11 : Ajoutons une manette !

 

Tutoriel présenté par : Jérémie F. Bellanger (Jay81)
Ecriture : 24 octobre 2014

Dernière mise à jour : 20 juin 2016

 

      Prologue

   Dans ce nouveau chapitre, nous allons voir comment ajouter le support d'une manette à notre jeu, car c'est quand même plus sympa pour jouer ! wink

   Dans le tuto précédent, j'avais attendu le chapitre 33 (auquel ce chapitre va souvent faire écho, même si le code a été sensiblement amélioré ici wink) pour gérer les manettes. Cela m'a semblé bien trop tardif pour tous ceux qui auraient voulu tester leur jeu dès le début, manette en main ! angel Cette fois-ci, je m'y prends donc beaucoup plus tôt ! cool

   Alors sinon, pour reprendre mes propos du chapitre 33 (que vous n'aurez pas forcément lu, si vous commencez directement ce nouveau tuto), c'était plutôt simple à faire en SDL 1.2, vu le nombre de tutos sur le sujet. Mais voilà, pour la SDL 2, il n'y a pas grand chose à l'heure actuelle... Et malheureusement, la doc officielle n'est pas encore très claire, je trouve (mais il faut bien leur laisser le temps de la constituer wink).

    C'est pourquoi, je vais vous donner un petit coup de pouce avec ce tuto, si vous aussi vous galérez ! cool Et vous verrez que ce n'est finalement pas beucoup plus compliqué. wink

 

   Voilà, maintenant, pour ce tuto, j'aurais pu m'arrêter à la détection de la manette, et voilà ! angel
   Sauf que souvent, on lance le programme sans la manette, et on se dit : "Mince, la manette ! surprise " et on la branche. Et là, on s'attend à ce que le programme la détecte et qu'on puisse jouer avec sans avoir rien à faire (et surtout pas relancer la programme ! indecision ).

   On va donc essayer de créer un système plus pro qui détecte les manettes (ou leur absence) on the go ! (Mais comme on le verra, il restera quand même un petit bug... qu'on arrivera peut-être à résoudre avec le temps et votre aide ! wink).

 

   Sinon, j'ai aussi réussi à gérer en même temps le D-PAD et le thumbpad de la manette Xbox 360 (car c'est avec cette manette, devenue plus ou moins standard que j'ai programmé ce tuto - vous pourrez cependant en essayer d'autres, mais il faudra certainement adapter le mapping des touches wink).

   J'ai donc créé un système avancé de détection : comme on ne peut pas utiliser le D-PAD et le thumbpad en même temps, à moins d'être un martien avec 4 mains laugh , l'utilisation du D-PAD désactive le thumbpad (pour éviter les conflits wink ). Il suffit alors de lâcher le D-PAD et d'utiliser le thumbpad pour le réactiver en 1/60ème de seconde (soit un tour de boucle, c'est important pour la réactivité cheeky ) ! Du coup, l'utilisateur lambda ne s'en rendra jamais compte, il se dira simplement que sa manette Xbox 360 marche correctement (et c'est le but laugh ).   

   Et voilà, donc on est parti !

 

      Le code

   On va commencer par aller dans le fichier defs.h, pour créer quelques nouvelles defines qui vont nous servir ici wink.

   On aura tout d'abord une def pour définir une dead zone dans le thumbpad de notre manette. Effectivement, le thumbpad n'est jamais réellement à 0. Même au repos, il dévie un peu. On va donc mettre en place une deadzone dans laquelle, on va considérer que le gamepad est au repos. On va prendre comme valeur 8000, ce qui peut paraître beaucoup, mais la valeur max renvoyée par le thumbpad dépasse les 34000 ! wink

   On va ensuite définir d'autres defs pour chacun des boutons de notre manette dont nous aurons besoin. Ce mapping concerne la manette Xbox 360 (dont vous trouverez le schéma du mapping ci-dessous). Si vous voulez tester une autre manette, il vous suffira de changer la valeur des touches ici, selon votre manette, et cela vous évitera d'aller chercher partout dans le code ! cool

 

Fichier : defs.h : Rajoutez :

//Dead zone de la manette pour éviter les mouvements involontaires
#define DEAD_ZONE 8000
 
//Mapping de la manette Xbox 360
#define BOUTON_HAUT 0
#define BOUTON_BAS 1
#define BOUTON_GAUCHE 2
#define BOUTON_DROITE 3
 
#define BOUTON_SAUT 10
#define BOUTON_ATTAQUE 12
#define BOUTON_PAUSE 4
#define BOUTON_QUIT 5

 

   Et voilà le mapping des touches de la manette Xbox 360 de Microsoft :

     Initialisation de notre joystick

   Il va maintenant nous falloir initialiser notre joystick, et c'est là que j'ai un peu galéré !

   Dans la SDL 1.2, on l'initialisait en même temps que la SDL. Mais maintenant, on n'initialise plus la SDL 2... frown

   Alors, en fait, il faut juste initialiser le sous-sytème Joystick ! cool

   Ensuite, on cherche s'il y a au moins un joystick connecté et on initialise le premier qui passe (pour le support multi-manette, je n'ai pas encore testé, mais ça doit être faisable en créant plusieurs variables Joystick). wink

 

Fichier : init.c : Rajoutez à la fin de la fonction init() :

// Initialise le sous-sytème de la SDL gérant les joysticks
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
 
//On cherche s'il y a des joysticks
if (SDL_NumJoysticks() > 0)
{
openJoystick();
}

 

          Fermeture de notre joystick

   Et bien entendu, comme on est des gens propres, on n'oublie pas de faire le ménage en partant ! wink

Fichier : init.c : Remplacez la fonction précédente par :

void cleanup()
{
//Nettoie les sprites de la map
cleanMaps();
 
/* Libère le sprite du héros */
cleanPlayer();
 
/* Ferme la prise en charge du joystick */
closeJoystick();
 
//On quitte SDL_Mixer 2 et on décharge la mémoire
Mix_CloseAudio();
Mix_Quit();
 
//On fait le ménage et on remet les pointeurs à NULL
SDL_DestroyRenderer(renderer);
renderer = NULL;
SDL_DestroyWindow(screen);
screen = NULL;
 
//On quitte SDL_TTF 2
TTF_Quit();
 
//On quitte la SDL
SDL_Quit();
}

  

     Gérons les inputs de notre manette !

   Bon, c'est bien, on a une manette, mais pour l'instant, elle ne sert à rien ! angel

   On va donc créer une nouvelle fonction dans notre fichier d'input, que nous nommerons simplement getJoystick() !

   Mais avant, on va rajouter une variable de type SDL_Joystick pour gérer les inputs du joystick et aussi une nouvelle variable locale : DPADinUse, qui nous permettra de switcher entre le D-Pad et le thumbpad, selon celui que le joueur utilise ! wink

Fichier : input.c : Rajoutez au début du fichier :

#include "prototypes.h"
 
//Manette
SDL_Joystick *joystick;
int DPADinUse = 0;
 
 
void openJoystick(void)
{
//On ouvre le joystick
joystick = SDL_JoystickOpen(0);
 
if (!joystick)
printf("Le joystick 0 n'a pas pu être ouvert !\n");
}
 
 
void closeJoystick(void)
{
/* Ferme la prise en charge du joystick */
if (SDL_JoystickGetAttached(joystick))
SDL_JoystickClose(joystick);
}

 

   Ensuite, nous définissons nos fonctions openJoystick() et closeJoystick() dont le nom résume bien ce qu'elles font ! laugh

     Fonction d'auto-détection

   Voilà, maintenant, il nous reste encore à faire appel à notre fonction getjoystick() à la place du clavier, si une manette est connectée. wink Pour cela, on va mettre à jour notre fonction gestionInputs().

   Mais que se passerait-il si on déconnectait la manette en cours de partie ? surprise

   A ce moment-là, le jeu ne répondrait plus ! broken heart

   On va donc rajouter 2 sécurités :
     - la première va détecter si on déconnecte la manette, et dans ce cas, fermer le support du joystick et revenir au clavier.
     - la seconde va détecter si on branche une nouvelle manette (s'il n'y en a pas déjà une), et si c'est le cas, il va l'initialiser pour qu'on puisse jouer avec tout de suite !

   Voici le code commenté, que je vous laisse lire :

Fichier : input.c : Remplacez la fonction précédente par :

void gestionInputs(Input *input)
{
/* On prend en compte les inputs (clavier, joystick... */
if (joystick != NULL)
{
//On vérifie si le joystick est toujours connecté
if (SDL_NumJoysticks() > 0)
getJoystick(input);
 
//Sinon on retourne au clavier
else
{
SDL_JoystickClose(joystick);
joystick = NULL;
}
 
}
//S'il n'y a pas de manette on gère le clavier
else
{
//On vérifie d'abord si une nouvelle manette a été branchée
if (SDL_NumJoysticks() > 0)
{
//Si c'est le cas, on ouvre le joystick, qui sera opérationnel au prochain tour de boucle
joystick = SDL_JoystickOpen(0);
 
if (!joystick)
printf("Couldn't open Joystick 0\n");
}
 
//On gère le clavier
getInput(input);
}
}

   Passons maintenant aux choses sérieuses, au nerf de la guerre, avec la fonction getjoystick()wink

   Je l'ai déjà pas mal commentée, donc je vous laisse la lire. smiley

Fichier : input.c : Ajoutez la fonction :

void getJoystick(Input *input)
{
SDL_Event event;
 
//Si on ne touche pas au D-PAD, on le désactive (on teste les 4 boutons du D-PAD)
if (SDL_JoystickGetButton(joystick, BOUTON_HAUT) == 0 && SDL_JoystickGetButton(joystick, BOUTON_BAS) == 0
&& SDL_JoystickGetButton(joystick, BOUTON_DROITE) == 0
&& SDL_JoystickGetButton(joystick, BOUTON_GAUCHE) == 0)
DPADinUse = 0;
 
/* On passe les events en revue */
while (SDL_PollEvent(&event))
{
 
 
if (event.type == SDL_QUIT)
exit(0);
 
 
else if (event.type == SDL_KEYDOWN)
{
switch (event.key.keysym.sym)
{
case SDLK_ESCAPE:
exit(0);
break;
 
default:
break;
 
}
}
 
else if (event.type == SDL_JOYBUTTONDOWN)
{
 
if (event.jbutton.button == BOUTON_SAUT)
input->jump = 1;
 
else if (event.jbutton.button == BOUTON_ATTAQUE)
input->attack = 1;
 
else if (event.jbutton.button == BOUTON_PAUSE)
input->enter = 1;
 
else if (event.jbutton.button == BOUTON_QUIT)
exit(0);
 
else if (event.jbutton.button == BOUTON_HAUT)
{
input->up = 1;
DPADinUse = 1;
}
 
else if (event.jbutton.button == BOUTON_BAS)
{
input->down = 1;
DPADinUse = 1;
}
 
else if (event.jbutton.button == BOUTON_GAUCHE)
{
input->left = 1;
DPADinUse = 1;
}
 
else if (event.jbutton.button == BOUTON_DROITE)
{
input->right = 1;
DPADinUse = 1;
}
 
}
 
else if (event.type == SDL_JOYBUTTONUP)
{
if (event.jbutton.button == BOUTON_PAUSE)
input->enter = 0;
 
else if (event.jbutton.button == BOUTON_SAUT)
input->jump = 0;
 
else if (event.jbutton.button == BOUTON_HAUT)
input->up = 0;
 
else if (event.jbutton.button == BOUTON_BAS)
input->down = 0;
 
else if (event.jbutton.button == BOUTON_GAUCHE)
input->left = 0;
 
else if (event.jbutton.button == BOUTON_DROITE)
input->right = 0;
 
}
 
//Gestion du thumbpad, seulement si on n'utilise pas déjà le D-PAD
else if (event.type == SDL_JOYAXISMOTION && DPADinUse == 0)
{
//Si le joystick a détecté un mouvement
if (event.jaxis.which == 0)
{
//Si le mouvement a eu lieu sur l'axe des X
if (event.jaxis.axis == 0)
{
//Si l'axe des X est neutre ou à l'intérieur de la "dead zone"
if ((event.jaxis.value > -DEAD_ZONE) && (event.jaxis.value < DEAD_ZONE))
{
input->right = 0;
input->left = 0;
}
//Sinon, de quel côté va-t-on ?
else
{
//Si sa valeur est négative, on va à gauche
if (event.jaxis.value < -DEAD_ZONE)
{
input->right = 0;
input->left = 1;
}
//Sinon, on va à droite
else if (event.jaxis.value > DEAD_ZONE)
{
input->right = 1;
input->left = 0;
}
}
}
 
//Si le mouvement a eu lieu sur l'axe des Y
else if (event.jaxis.axis == 1)
{
//Si l'axe des Y est neutre ou à l'intérieur de la "dead zone"
if ((event.jaxis.value > -DEAD_ZONE) && (event.jaxis.value < DEAD_ZONE))
{
input->up = 0;
input->down = 0;
}
//Sinon, de quel côté va-t-on ?
else
{
//Si sa valeur est négative, on va en haut
if (event.jaxis.value < 0)
{
input->up = 1;
input->down = 0;
}
//Sinon, on va en bas
else
{
input->up = 0;
input->down = 1;
}
}
}
}
}
 
}
 
}

   Bon, vous remarquerez que pour l'essentiel, la boucle des events ressemble à celle du clavier (avec des if au lieu des case, car c'est plus simple ici wink).

   Pour tout ce qui est boutons (et vous aurez remarqué que le D-PAD de la manette Xbox 360 est maintenant assimilé à 4 boutons alors qu'il était considéré comme un HAT dans la version 1.2 de la SDL), il s'agit de regarder s'ils sont enfoncés ou non, et dans ce cas de changer la valeur de nos inputs, un peu comme pour le clavier. smiley

   Pour le thumbpad, il s'agit de voir s'il y a eu un mouvement sur l'un de ses axes (X - gauche/droite ou Y - haut/bas) et si ce mouvement sort de notre deadzone.

   S'il n'y a pas de mouvement, tous les inputs reviennent à zéro, et sinon, on passe à 1 l'input correspondant à l'axe du stick poussé.

 

   Mais là, où le problème arrive, c'est que sans l'utilisation de notre variable DPADinUse (qui aurait pu simplement être un booléen en C++ wink), il y aurait conflit entre le D-PAD et le thumbpad, le thumbpad remettant les inputs à 0, alors qu'il y a parfois appui sur une touche du D-PAD. C'est pourquoi, on met DPADinUse à 1 (vrai) dès qu'on détecte un appui sur une touche du D-PAD. En outre, la gestion du thumbpad ne se fait plus, si cette condition est vraie (DPADinUse == 1).

   Mais pour pouvoir réutiliser le thumbpad, si on lâche le D-PAD, on teste tout au début du programme, s'il n'y a aucun appui sur aucune des touches du D-PAD, et si c'est le cas, on passe DPADinUse à 0 (faux) pour réactiver éventuellement le thumbpad. cool

   Ainsi, le joueur peut passer de l'un à l'autre librement n'importe quand, et ça fait quand même plus pro ! wink

   Maintenant, là où il reste encore un bug que je n'ai pas réussi à résoudre (vient-il de moi ou de la SDL ? frown), c'est que si on débranche et qu'on rebranche la manette (avec le système de détection auto que nous verrons ci-après), le thumbpad ne réagit plus ?... angry Si vous trouvez la solution à ce problème, merci de la poster sur le forum ! smiley

   Bien, il ne nous reste plus qu'à mettre à jour nos prototypes, comme d'habitude :

Fichier : prototypes.h : Remplacez par :

#ifndef PROTOTYPES
#define PROTOTYPES
 
#include "structs.h"
 
/* Catalogue des prototypes des fonctions utilisées.
On le complétera au fur et à mesure. */
 
extern void centerScrollingOnPlayer(void);
extern void changeLevel(void);
extern void cleanMaps(void);
extern void cleanPlayer(void);
extern void cleanup(void);
extern void closeJoystick(void);
extern void delay(unsigned int frameLimit);
extern void drawGame(void);
extern void drawImage(SDL_Texture *, int, int);
extern void drawMap(int);
extern void drawPlayer(void);
extern void drawTile(SDL_Texture *image, int destx, int desty, int srcx, int srcy);
extern void gestionInputs(Input *input);
extern SDL_Texture *getBackground(void);
extern int getBeginX(void);
extern int getBeginY(void);
extern void getInput(Input *input);
extern void getJoystick(Input *input);
extern int getLevel(void);
extern int getMaxX(void);
extern int getMaxY(void);
extern GameObject *getPlayer(void);
extern int getPlayerDirection(void);
extern int getPlayerx(void);
extern int getPlayery(void);
extern SDL_Renderer *getrenderer(void);
extern int getStartX(void);
extern int getStartY(void);
extern void init(char *);
extern void initializePlayer(int newLevel);
extern void initMaps(void);
extern void initPlayerSprites(void);
extern void loadGame(void);
extern SDL_Texture *loadImage(char *name);
extern void loadMap(char *name);
extern void mapCollision(GameObject *entity);
extern void openJoystick(void);
extern void setNombreDeVies(int valeur);
extern void setNombreDetoiles(int valeur);
extern void setStartX(int valeur);
extern void setStartY(int valeur);
extern void SetValeurDuNiveau(int valeur);
extern void updatePlayer(Input *input);
 
 
#endif

   Voilà, plus qu'à compiler et à lancer le programme ! wink   

   Vous pouvez maintenant profiter de votre jeu de plateformes avec votre manette préférée ! cool

   Elle est pas belle, la vie ?! cool

   Je vous dis donc à bientôt pour le chapitre 12 ! angel

                                                                            Jay 

 

 

Abonnez-vous et devenez Premium pour lire la fin de ce tuto ainsi que tous les autres tutos du site !

 

 

   Et voici maintenant la liste des chapitres suivants, de ce tuto, auxquels vous aurez accès :

- Big Tuto SDL 2 - Chapitre 12: Ajoutons un monstre
- Big Tuto SDL 2 - Chapitre 13 : Affichons le HUD!
- Big Tuto SDL 2 - Chapitre 14: Monte le son !
- Big Tuto SDL 2 - Chapitre 15: Power-ups et tiles spéciales !
- Big Tuto SDL 2 - Chapitre 16: Des plateformes volantes !
- Big Tuto SDL 2 - Chapitre 17: Niveaux et checkpoints
- Big Tuto SDL 2 - Chapitre 18: Ajoutons des menus !
- Big Tuto SDL 2 - Chapitre 19: Des shurikens !
- Big Tuto SDL 2 - Chapitre 20: Des explosions !
- Big Tuto SDL 2 - Chapitre 21: Ajoutons des pentes ! 

 

Et voilà le résultat final auquel vous parviendrez à la fin de ce Tuto !

 

   @ bientôt ! wink

                Jay

 

 

Connexion

CoalaWeb Traffic

Today139
Yesterday282
This week933
This month3226
Total1742433

19/04/24