07/05/2023 : Malheureusement, suite à un manque de financement et à des échéances prochaines impliquant de refaire TOUT le site en l'adaptant à une nouvelle plateforme pour pallier les risques croissants de sécurité, il est pour l'instant prévu que le site ferme prochainement ses portes...
 
13/06/2023 : Finalement, le site a été refinancé pour une nouvelle année complète. En l'état, il est fonctionnel mais notre hébergeur ne nous permet pas une mise à jour vers Joomla 4 ou php 8 (il a été racheté et n'est plus que l'ombre de lui-même...). Cela nous laisse cependant un peu de temps pour trouver une solution.

 

 
 

 

Chapitre 33

Ajoutons une manette en SDL 2 ! 

 

Tutoriel présenté par : Jérémie F. Bellanger (Jay)
Dernière mise à jour : 21 juillet 2014

 

   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

   Je l'avais déjà fait en SDL 1.2, et vu le nombre de tutos sur le sujet, ça avait été plutôt simple. 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

 

   Voilà, maintenant, pour ce tuto, j'aurais pu m'arrêter à la détection de la manette, et voilà ! :D
   Sauf que souvent, on lance le programme sans la manette, et on se dit : "Mince, la manette ! :shock: " 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 ! :mrgreen: ).

   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 :lol: , l'utilisation du D-PAD désactive le thumbpad (pour éviter les conflits ;) ). 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, important pour la réactivité ;) ) ! 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 :P ).   

 

   Eh voilà, donc on est parti !

   

 

 

      De nouvelles defs

   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

//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 :

 

 

 

      Mise à jour de notre structure Gestion jeu

   On va ensuite simplement rajouter une variable de type SDL_Joystick à notre structure Gestion :

 

Fichier : structs.h / Gestion

/* Structure pour gérer le niveau */
typedef struct Gestion
{

    SDL_Window *screen;
	SDL_Renderer *renderer;

	//Manette
	SDL_Joystick *joystick;

 

     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 / 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)
    {
        //On ouvre le joystick
        jeu.joystick = SDL_JoystickOpen(0);

        if (!jeu.joystick)
            printf("Le joystick 0 n'a pas pu être ouvert !\n");

    }

 

          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 / cleanup()

    
    /* Ferme la prise en charge du joystick */
    if (SDL_JoystickGetAttached(jeu.joystick))
        SDL_JoystickClose(jeu.joystick);

 

     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 ajouter notre structure Gestion dans l'en-tête du fichier, afin de pouvoir avoir accès au fichier, et nous allons aussi créer 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.h

 
   
    int DPADinUse = 0;
    extern Gestion jeu;

 

   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 / nouvelle fonction

    

void getJoystick()
{
	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(jeu.joystick, BOUTON_HAUT) == 0 &&  SDL_JoystickGetButton(jeu.joystick, BOUTON_BAS) == 0
        && SDL_JoystickGetButton(jeu.joystick, BOUTON_DROITE) == 0 && SDL_JoystickGetButton(jeu.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 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

 

     Le main() et sa fonction d'auto-détection

   Voilà, maintenant, il nous reste encore à faire appel à notre fonction getjoystick() à la place du clavier, dans le main(), si une manette est connectée. wink

   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 : main.c / main()

 
   
/* Boucle infinie, principale, du jeu */
while (go == 1)
{

	/* On prend en compte les inputs (clavier, joystick... */
	if (jeu.joystick != NULL)
        {
            //On vérifie si le joystick est toujours connecté
            if(SDL_NumJoysticks() > 0)
                getJoystick();

            //Sinon on retourne au clavier
            else
            {
                SDL_JoystickClose(jeu.joystick);
                jeu.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
                jeu.joystick = SDL_JoystickOpen(0);
                if (!jeu.joystick)
                    printf("Couldn't open Joystick 0\n");
            }
            //On gère le clavier
            getInput();
        }

 

   Et voilà, en fait, ce n'était pas si compliqué.

   Maintenant, pour finir, n'oublions pas notre en-tête :

 

Fichier : main.h

 
   
   extern void getJoystick(void);

 

   Eh voilà, vous pourrez maintenant profiter de votre jeu de plateformes avec votre manette préférée ! cool

   Elle est pas belle, la vie ?! cool

 

   Avant de refermer ce chapitre, vous noterez que j'ai rajouté la possibilité de démarrer le jeu en appuyant sur la touche A (jump) de la manette Xbox 360 (dans la fonction d'update du menu), car c'est quand même mieux ainsi ! Et sinon, dans le prochain chapitre, on fera encore quelques petites améliorations, comme la possibilité de choisir son niveau de départ (mieux si vous en avez créé 99 ! cheeky), un menu Pause plus développé, avec la possibilité de retourner à l'écran-titre pour changer de niveau, et si on a le temps, une nouvelle anim' d'explosion quand les monstres meurent ! wink

 

   @ bientôt sur Meruvia !

                         Jay.

  

Connexion

CoalaWeb Traffic

Today44
Yesterday232
This week1070
This month3363
Total1742570

20/04/24