Libgdx Game

dimanche 12 mai 2013

Réaliser un menu d’entrée pour un jeu ou une application #11 (1ère partie)



Introduction
Ce n’est pas intéressant de savoir comment déplacer Mario dans une carte et ne pas savoir comment faire un bouton, bien que jouer avec Mario est plus rigolo que de taper sur des boutons !
Il y a plusieurs façons de faire un menu avec Libgdx, on va présenter deux façons de faire, la première a travers cet article et la deuxième à travers l’article qui le suivra.
1ère méthode :
Cette 1ère méthode est basée sur le fait de dessiner le menu qu’on veut réaliser puis de l’implémenter de A à Z, en utilisant des images et des icônes de notre choix par exemple : Je dessine des formes sous un éditeur tel que Paint ou Gimp puis je l’ai charge sous forme de texture afin de les manipuler en tant que boutons et éléments de menu
Cette façon de faire nous permet de réaliser un menu d’un jeu ou d’une application  qui correspond exactement à ce qu’on veut faire sans être limiter par des formes ou par un design bien précis c’est-à-dire, on peut dessiner notre menu puis le réaliser telle qu’il est.
Je conseille cette façon de faire pour la réalisation de petits menu qui contiennent quelques boutons et avec minimum d’informations à afficher car créer un menu de cette façon nécessite de tout prévoir et contrôler ce qui nous demande de cracher beaucoup  de code source.
Suivez ses étapes et votre menu sera créer (comme par magie !)
1er Etape : Dessiner le menu

Attention : Avant de dessiner il faut choisir la taille repère c’est-à-dire la taille de l’écran sur lequel vous allez dessiner votre menu. On va choisir pour cet exemple la taille 480x320 comme taille repère.
Comme vous le savez surement, un menu d’un Jeu/Application doit fonctionner sur plusieurs appareils

Voici des tailles de quelques écrans d’appareils mobiles :
Smartphone
Taille en Pixels
Taille en Pouce
HTC Hero
320×480
3,2
Nokia N900
800×480
3,5
Galaxy Notes
1280×800
5,3
Galaxy Tab 10,1
1280×800
10,1

Un designer est le bienvenu pour effectuer cette partie 
Voici à quoi ressemble mon menu pour cet exemple 



Bon!, je suis loin d’être un designer.

2eme Etape : Préparer les éléments de menu
Les éléments des menus sont des textures chacune à part. Une règle important pour charger ces textures c’est qu’ils aient une taille sous la forme 2xx2y , donc il faut concevoir la taille de notre bouton comme ça nous chante puis compléter avec un vide la taille de ce dernier pour qu’elle soit sous la forme de 2xx2y
Par exemple la taille de mon bouton est 120x45, il faut alors compléter l’image contenant ce bouton avec du vide en utilisant un éditeur d’image pour qu’elle devient 128x64  si la taille de mon image est de 258x130 alors il faut la compléter avec du vide pour qu’elle devienne 512x256 et ainsi de suite.
Si vous n’avez aucune notion sur l’utilisation des éditeurs d’images, je vous conseille de voir ce petit tutoriel qui contient seulement les notions nécessaires concernant l’éditeur GIMP et Paint pour développer avec à l’aise avec Libgdx.

Voici les textures qui représentent les éléments de mon menu

Boutons 



J’ai choisi la forme nuage pour montrer qu’on peut choisir n’importe qu’elle forme, on n’est pas limité par la forme rectangle
ici pour télécharger le bouton

Bouton cliqué







Il faut aussi préparer le bouton sur sa forme cliqué c’est-à-dire sa couleur lorsque on lui clique dessus et on maintient le clic
ici pour télécharger le bouton cliqué

Arrière-Plan 





















Un arrière-plan qui correspond au contexte du menu
ici pour télécharger l’arrière-plan

Bouton retour







Un bouton qu’on clique dessus pour retourner au menu principal
ici pour télécharger

3eme étape : Cracher du code source

On va expliquer les parties les plus importants, si vous avez suivis les précédents tutoriels vous n’auriez aucun mal à comprendre ce code sinon, je vous propose de voir ce tutorielpour le graphique ainsi que ce tutoriel pour la gestion des entrées.
+ Gérer la position des éléments du menu selon la taille d’écran :
Quand on veut placer un élément (exemple : image, texte)  dans notre écran il faut toujours le référencer par rapport à la taille de celui-ci par exemple si on veut placer notre bouton au milieu de l’écran sur un écran de résolution 480x320 comme le montre cette image :


Il faut affecter la position de l’image comme suite :
Position.x (sur l’axe X) = largeur écran /2
Position.x(sur l’axe y) = hauteur écran /2
Au lieu de faire :
Position.x = 240
Position.y = 160
Car ceci va poser un problème si la taille de l’écran sur laquelle mon application va s’exécuter est différente de 480x320 comme le montre ce schéma :


Si je veux placer un élément dans un écran et que le rapport de la position de cet élément et l’écran reste le même quel que soit l’écran comme explique l’image ci-dessus :



Il suffit juste de faire :
Position.x = (60 x largeur_ecran_actuel )/largeur_ecran_repere
Position.y= (60 x hauteur_écran_actuel)/largeur_ecran_repere
Dans notre exemple
Position.x = (60 x largeur_ecran_actuel )/480
Position.y= (60 x hauteur_écran_actuel)/320

Ecran 480x320
Ecran 1024x768
Position.x = 60 x 480 /480 = 60
Position.y = 60 x320 /320  = 60

Position.x = 60 x 1024 /480 = 128
Position.y = 60 x 768 / 320  = 144



Puisque tout cela va se répéter pour toutes positions d’éléments le mieux c’est de le mettre dans une fonction
* Voici la première fonction qui maintient la proportion sur l’axe des X
private float xUnite(float x)
{
       return x*largeur_ecran/480f;
}
      
* Voici la première fonction qui maintient la proportion sur l’axe des Y
private float yUnite(float y)
{
       return y*hauteur_ecran/320;
}
Pour placer notre bouton on fait :
xposBouton1 = xUnite(176);
yposBouton1 = yUnite(223);

+ Gérer la taille des éléments du menu selon la taille de l’écran
Une fois la position de notre bouton est gérée en s’intéresse à sa taille ce schéma donne plus d’explication :


Les deux fonctions définies au-dessus peuvent être utilisées pour maintenir la proportion entre la taille de l’écran actuelle et la taille de l’écran repère.
Taille.x = (128 x largeur_ecran_actuel )/480
Taille.y =(64 x hauteur_écran_actuel)/320



Ecran 480x320
Ecran 1024x768
taille.x = 128 x 480 /480 = 128
taille.y = 64 x320 /320  = 64

taille.x = 60 x 1024 /480 = 273
taille.y = 60 x 768 / 320  = 153


* Pour définir la taille de l’écran on fait :
boutonSprite.setSize(xUnite(128), yUnite(64));

+ Gérer le clic sur bouton et le bouton cliqué
L’astuce ici c’est de changer l’image lorsqu’on clique sur le bouton par une image identique au bouton mais dont la couleur est différente.
* pour savoir si un bouton est cliqué
public boolean touchDown(int x, int y, int pointer, int bouton) {
    // si un doigt est sur le bouton
    if(x>xUnite(180) && x < xUnite(300) && y>yUnite(40) && y<yUnite(88) )
    {
          cliqueBouton1=true;
    }
}
* Pour dessiner le bouton ou le bouton une fois cliqué :
if(!cliqueBouton1)
{
       boutonSprite.setPosition(xposBouton1, yposBouton1);
       boutonSprite.draw(batch);
}else
{
       boutonCliqueSprite.setPosition(xposBouton1, yposBouton1);
       boutonCliqueSprite.draw(batch);
}
Vous remarquez qu’on utilise la classe Sprite au lieu de Texture parce qu’elle possède plus de fonctionnalités : préciser la taille, la position…
Si on relâche (déclique) le bouton alors que notre doigt (la souris en Desktop) est positionner sur le bouton ça veut dire qu’on a appuyé sur le bouton
Mais si on  relâche (déclique) le bouton alors que notre doigt (la souris en Desktop) est positionner hors du bouton ça veut dire qu’on annuler notre clique
* Voici comment le faire
public boolean touchUp(int x, int y, int pointer, int bouton) {
// si lève le doigt du bouton
if(x>xUnite(180) && x < xUnite(300) && y<yUnite(88) && y> yUnite(40))
    {
      
       page=1; // on active la page 1 
    }
    cliqueBouton1 = false;
}

* Voici le code source complet de l’exemple
package my.works.Article;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class MenuMethode1 implements ApplicationListener{

       private Sprite boutonSprite;
       private Sprite boutonCliqueSprite;
       private Sprite arrierePlanSprite;
       private Sprite boutonRetourSprite;
       private BitmapFont font;
       private SpriteBatch batch;
      
       private float largeur_ecran;
       private float hauteur_ecran;
      
       private float xposBouton1;
       private float xposBouton2;
       private float xposBouton3;
       private float xposBoutonRetour;
      
       private float yposBouton1;
       private float yposBouton2;
       private float yposBouton3;
       private float yposBoutonRetour;
      
       private boolean cliqueBouton1;
       private boolean cliqueBouton2;
       private boolean cliqueBouton3;
      
       private float xDecalage;
       private float yDecalage;
      

       private int page;
      
       private String texteBouton1;
       private String texteBouton2;
       private String texteBouton3;
      
      
       // Fonction qui maintien le rapport entre les positions Y
       // vis-à-vis de la taille de l'écran
       private float xUnite(float x)
       {
             return x*largeur_ecran/480f;
       }
      
       // Fonction qui maintien le rapport entre les positions Y
       // vis-à-vis de la taille de l'écran
       private float yUnite(float y)
       {
             return y*hauteur_ecran/320;
       }
      
      
       @Override
       public void create() {
            
             // Obtenir la taille de l'écran actuelle
             largeur_ecran = Gdx.graphics.getWidth();
             hauteur_ecran = Gdx.graphics.getHeight();
            
             batch = new SpriteBatch();
            
             // Charger Texture dans Sprite
             boutonSprite =new Sprite(new Texture(Gdx.files.internal("bouton.png"))) ;
             boutonCliqueSprite = new Sprite(new Texture(Gdx.files.internal("boutonclique.png"))) ;
             arrierePlanSprite = new Sprite(new Texture(Gdx.files.internal("arriereplan.png")));
             boutonRetourSprite = new Sprite(new Texture(Gdx.files.internal("retour.png")));
             
             boutonSprite.setSize(xUnite(128), yUnite(64));
             boutonCliqueSprite.setSize(xUnite(128), yUnite(64));
             arrierePlanSprite.setSize(xUnite(512), yUnite(512));
             boutonRetourSprite.setSize(xUnite(64), yUnite(64));
                   
             // La police pour le texte
             font = new BitmapFont();
             font.setColor(Color.DARK_GRAY);
             font.setScale(xUnite(1), yUnite(1)); // définir la taille du texte selon l'écran
            
             xDecalage = xUnite(40); // pour gérer le décalage de positionnement entre font et sprite
             yDecalage = yUnite(40);
            
             // Texte des boutons 1, bouton 2, bouton 3
             texteBouton1 = "Start";
             texteBouton2 = "Options";
             texteBouton3 = "Bonnus";
            
             xposBouton1 = xUnite(176); // Position du bouton 'StartGame'
             yposBouton1 = yUnite(223);

             xposBouton2 = xUnite(176); // Position du bouton 'Options'
             yposBouton2 = yUnite(150);
            
             xposBouton3 = xUnite(176); // Position du bouton 'Bonus'
             yposBouton3 = yUnite(74);
            
             xposBoutonRetour = xUnite(0);  // Position du bouton 'Retour'
             yposBoutonRetour = yUnite(260);
             boutonRetourSprite.setPosition(xposBoutonRetour, yposBoutonRetour);
      
       }
      
       public void manipulerMenu()
       {
             Gdx.input.setInputProcessor(new InputProcessor() {

                    @Override
                    public boolean touchUp(int x, int y, int pointer, int bouton) {
                           if(x>xUnite(180) && x < xUnite(300) && y<yUnite(88) && y> yUnite(40))
                           {
                                  // le bouton 1 (startGame) a été cliqué
                                  page=1;
                           }
                           if(x>xUnite(180) && x < xUnite(300) && y>yUnite(115) && y<yUnite(160))
                           {
                                  // le bouton 2 (Options) a été cliqué
                                  page=2;
                           }
                          
                           if(x>xUnite(180) && x < xUnite(300) && y>yUnite(190) && y<yUnite(235))
                           {
                                  // le bouton 3 (Bonus) a été cliqué    
                                  page=3;
                           }
                           if(x>xUnite(0) && x<xUnite(64) && y>yUnite(0) && y<yUnite(64))
                           {
                                  // le bouton retour a été cliqué
                                  page=0;
                           }
                          
                           cliqueBouton1 = false;
                           cliqueBouton2 = false;
                           cliqueBouton3 = false;
                          
                           return false;
                    }

                    @Override
                    public boolean touchDown(int x, int y, int pointer, int bouton) {
                   
                           if(x>xUnite(180) && x < xUnite(300) && y>yUnite(40) && y<yUnite(88) )
                           {
                                  cliqueBouton1=true;
                           }
                           if(x>xUnite(180) && x < xUnite(300) && y>yUnite(115) && y<yUnite(160))
                           {
                     cliqueBouton2=true;
                           }
                           if(x>xUnite(180) && x < xUnite(300) && y>yUnite(190) && y<yUnite(235))
                           {
                                  cliqueBouton3=true;
                           }
                           return false;
                    }
                   
                    @Override
                    public boolean touchDragged(int arg0, int arg1, int arg2) {
                           return false;
                    }
                   
                    @Override
                    public boolean scrolled(int arg0) {
                           return false;
                    }
                   
                    @Override
                    public boolean mouseMoved(int arg0, int arg1) {
                           return false;
                    }
                   
                    @Override
                    public boolean keyUp(int arg0) {
                           return false;
                    }
                   
                    @Override
                    public boolean keyTyped(char arg0) {
                           return false;
                    }
                   
                    @Override
                    public boolean keyDown(int arg0) {
                           return false;
                    }
             });
       }
       public void dessinerMenu()   // dessiner le menu
       {
      
             batch.begin();  // obligatoire pour commencer un dessin sur le SpriteBatch
      
       // arrierePlan
             arrierePlanSprite.draw(batch);
            
       // bouton 1
             if(!cliqueBouton1)
             {
                    boutonSprite.setPosition(xposBouton1, yposBouton1);// fixer la position
                    boutonSprite.draw(batch);                          // puis le dessiner
             }else
             {
                    boutonCliqueSprite.setPosition(xposBouton1, yposBouton1);
                    boutonCliqueSprite.draw(batch);                         
             }
      
       // bouton 2
             if(!cliqueBouton2
             {
                    boutonSprite.setPosition(xposBouton2, yposBouton2);// fixer la position
                    boutonSprite.draw(batch);                          // puis le dessiner
             }else
             {
                    boutonCliqueSprite.setPosition(xposBouton2, yposBouton2);
                    boutonCliqueSprite.draw(batch);
             }
            
       // bouton 3
             if(!cliqueBouton3)
             {
                    boutonSprite.setPosition(xposBouton3, yposBouton3); // fixer la position
                    boutonSprite.draw(batch);                           // puis le dessiner
             }else
             {
                    boutonCliqueSprite.setPosition(xposBouton3, yposBouton3);
                    boutonCliqueSprite.draw(batch);
             }
      
       // texte du bouton1
      
             font.draw(batch, ""+texteBouton1, xposBouton1+xDecalage, yposBouton1+yDecalage);
            
       // texte du bouton2
      
             font.draw(batch, ""+texteBouton2, xposBouton2+xDecalage, yposBouton2+yDecalage);
            
       // texte du bouton3
      
             font.draw(batch, ""+texteBouton3, xposBouton3+xDecalage, yposBouton3+yDecalage);
            
             batch.end();  // obligatoire pour finir le dessin sur un SpriteBatch
            
       }
      
       public void dessinerPage(int page)
       {
             batch.begin();
            
             if(page == 1)  // si on est à la page Game
                    font.draw(batch, "The Game", xUnite(200), yUnite(320));  // dessiner le titre de la page 1
             if(page == 2)  // si on est à la page Options
                    font.draw(batch, "Options", xUnite(200), yUnite(320)); // dessiner le titre de la page 2
             if(page == 3)  // si on est à la page Bonus
                    font.draw(batch, "Bonus", xUnite(220), yUnite(320)); // dessiner le titre de la page 3
            
             boutonRetourSprite.draw(batch);
             batch.end();
       }
       @Override
       public void dispose() {
             font.dispose();
             batch.dispose();
       }

       @Override
       public void pause() {
            
       }

       @Override
       public void render() {
            
             Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
             Gdx.gl.glClearColor(1, 1, 1, 1);
      
             manipulerMenu();  // gestion des input
            
             switch(page// dans quelle page je me situe ?
             {
             case 0:              // Contenu de la page menu
                    dessinerMenu();
                   
                    break;
             case 1:             // Contenu de la page Game
                    dessinerPage(1);
                    break;
      
             case 2:             // Contenu de la page Options
                    dessinerPage(2);
                    break;
            
             case 3:             // Contenu de la page Bonus
                    dessinerPage(3);
                    break;
             }
            
       }
      

       @Override
       public void resize(int arg0, int arg1) {
       }

       @Override
       public void resume() {
      
            
       }

}


Conclusion
On a vu dans ce tutoriel une façon de faire un petit menu pour une application ou un jeu et comment se déplacer à travers les pages de ceci. Dans un prochain article on va voir une autre façon de faire des boutons et des menus. Si vous avez des remarques, des propositions ou des questions n’hésitez pas à les poster en commentaire. Merci pour votre lecture.