Libgdx Game

samedi 22 décembre 2012

Faire une Animation avec Libgdx #9


Introduction

On va voir dans cet article tout ce qui concerne les fonctionnalités reliées aux animations 2D que possède notre bibliothèque Libgdx.
Faire des animations est très pratique si on veut développer un jeu. Une animation n’est que l’affichage d’un ensemble de textures (images statiques) d’une manière séquentielle et à une certain vitesse, et cela pour faire allusion à un mouvement.

Eléments d’une animation 2D ?

Une animation va dépendre de  trois caractéristiques :
- Nombre des images constituants une animation
- Le temps que va prendre une animation
- Le temps que prend chaque image et qu’on doit le calculer à partir de la première et la deuxième caractéristique,
et qui va déterminer la vitesse d’affichage des images constituant l’animation

Les utilisations d’une animation

Si on veut développer un simple jeu d’arcade, quatre à cinq textures (images) suffiront par contre si on opte pour un jeu plus réaliste, on aura besoins d’un nombre beaucoup plus de texture (images).
Il est préférable que toutes les images soient contenues dans une seule texture (fichier) : spritesheet, car manipuler une seule texture est plus optimisant pour le rendering.

Commet faire une animation avec libgdx ?

Penchant, un petit peu sur le code source

* Pour faire une animation on doit utiliser la class Animation, lors de l’instanciation de cette classe (dans la méthode create() ) deux paramètres doivent être indiqués

1er Paramètre : c’est le temps (en seconde) que prend chaque image pour s’afficher dans le cadre d’une animation
Une petite astuce pour déterminer un temps convenable  pour l’affichage de chaque image :
 temps = temps Total de l’animation / nombre d’image

Exemple : Je veux faire une animation qui se déroule pendant une seconde et cela à partir 5 images résultat 
Le temps d’affichage de chaque image = 1/5 = 0.2
Bien sûr, ceci n’est pas toujours applicable, ça varie selon le contexte.

2eme Paramètre c’est le tableau qui contient la suite des images qui vont être affichées dont le cadre d’une animation, les images doivent être de la même taille.

* Code source :
Animation animation;
animation = new Animation(temps, tableau_images);

Une fois l’animation instanciée dans la méthode create(), il faut se diriger vers la méthode render() pour effectuer l’affichage de l’animation, l’affichage s’effectue  en extrayant à chaque render une image à un instant précis et fixe , puis l’afficher en utilisant le Spritebatch

* Pour récupérer l’image d’une animation à un instant précis, la classe animation possède la méthode getKeyFrame qui possède deux paramètres

1er paramètre : c’est l’instant de l’image à récupérer

2eme paramètre : c’est un boolean qui indique si l’animation est finie ou bien infinie.

*Code source :
temps += Gdx.graphics.getDeltaTime();                   
regionCourante = animation.getKeyFrame( temps, true);  
batch.begin();
batch.draw(regionCourante, 100, 100);                    
batch.end();

Exemple 1 d’une animation

Voici un exemple d’une animation faite à partir d’un spritesheet dédié pour un jeu d’arcade, elle est constituée de cinq images contiguës et de même taille.











N’oubliez pas que la texture (spritesheet) doit avoir une résolution de 2x x 2y donc, il faut compléter l’image par le vide en utilisant un éditeur tel que : PAINT, ou encore GIMP
Encore une remarque, il faut aussi rendre le fond de la texture transparent et ceci en utilisant l’éditeur  GIMP par exemple

Voici à quoi ressemble une texture spritesheet prête à être charger dans une texture dans le cadre de notre exemple:

 Vous pouvez la télécharger ici

* Maintenant voici le code source de notre exemple vous allez trouver des détails juste en dessous

package my.works.Article;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;


public class AnimationExemple1 implements ApplicationListener{

       private static final int nombre_image = 5;
       private static final float duree_animation = 1f;
      
       private static final int largeur_image = 58;
       private static final int hauteur_image = 68;

       private SpriteBatch batch;
       private Texture sheetTexture;
       private Animation animation;
      private TextureRegion regionCourante;
       private TextureRegion tabRegion[];
      
       private  float   temps;
      
       @Override
       public void create() {
       // Initialisation  
           batch = new SpriteBatch();
           sheetTexture = new Texture(Gdx.files.internal("sumoSheet.png"));
       // # <<1>>   
           TextureRegion[][] tmp = TextureRegion.split(sheetTexture, largeur_image, sheetTexture.getHeight());
           tabRegion = new TextureRegion[nombre_image];
           int index = 0;
       // # <<2>>  
           for(int j = 0; j < 5; j++) {
           tabRegion[index++] = tmp[0][j];
        }
      // # <<3>>   
          animation = new Animation(duree_animation/nombre_image, tabRegion);
          temps=0.0f;
       }

       @Override
       public void dispose() {
       }

       @Override
       public void pause() {
       }

       @Override
       public void render() {
         Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);   
         Gdx.gl.glClearColor(0, 0, 0, 0);
   
    // # <<4>>
         temps += Gdx.graphics.getDeltaTime();                   
         regionCourante = animation.getKeyFrame(temps, true);  
    // # <<5>>
         regionCourante.setRegion(regionCourante, 0, 0, largeur_image, hauteur_image);
    // # <<6>>    
         batch.begin();
         batch.draw(regionCourante, 0, 0);                    
         batch.end();
       }

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

* Maintenant voici comment traiter le spritesheet à travers le code source pour construire notre animation

<<1>> : Premièrement, on va découper la texture et mettre chaque image dans un tableau de texture cela est possible grâce à la méthode split() qui va deviser une texture en plusieurs régions de même hauteur et de même largeur (par forcément hauteur = largeur), la méthode split() prend les paramètre suivant :
1er paramètre   : La texture qu’on va diviser par régions
2eme paramètre : La largeur de chaque région
3eme paramètre : La hauteur de chaque région

<<2>> : On doit transformer le tableau de régions qui contient les images constituants notre animation en un tableau d’une seule dimension.

<<3>> : ensuite, on instancié la classe Animation qui prend deux paramètres
1er paramètre : Le temps que va prendre chaque image constituant notre animation, et c’est là que vous pouvez  utiliser l’astuce montrée précédemment pour calculer ce temps.
2eme paramètre : Le tableau transformé dans <<2>> et qui contient les images de l’animation
Puis, initialisation de la variable temps qui va être utilisée dans la méthode render()

<<4>> : à chaque rendu, on extrait une région du tableau à un instant précis et cela pour l’afficher, ceci est faisable grâce à la méthode getKeyFrame() qui nous retourne Une TextureResgion et qui a comme paramètre
1er paramètre : l’instant de l’extraction de la région, meilleur façon de l’incrémenter est d’utiliser l’écart du temps entre la précédente l’actuelle extraction, et a travers la variable temps on peut manipuler la vitesse de notre animation.
2eme paramètre : est booléen qui va déterminer si l’animation est infinie ou bien finie.

<<5>> : Ici on précise la résolution de la région qu’on veut afficher

<<6>> : La région est prête à être dessiné

* Voici résultat sous Desktop :
Bien sûr, il faut la classe lanceur pour lancer l’application sous Desktop voir précédant tutoriel





* Voici le résultat sous Android :
Bien sûr, il faut la classe main pour lancer l’application sous Android voir précédant tutoriel





Exemple 2 d’une animation

Dans le ce deuxième exemple on va réaliser une animation manipulée, on va utiliser qui contient tous les déplacements  d’un personnage de jeu RGP.

* Code source :
package my.works.Article;

import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

public class AnimationExemple2 implements ApplicationListener{

       private static final int nb_colone = 4;
       private static final int nb_ligne = 8;
       private static final int nombre_image_animation = 4;
       private static final float duree_animation = 1f;
       private  float vitesse_lapin = 0.02f;
      
       private SpriteBatch batch;
       private Texture sheetTexture;
       private Animation animationLapin[];
      private TextureRegion regionCourante,regionInitial;
      
       private  int largeur_texture ;
       private  int hauteur_texture;

       private  float   temps;
      
       private float xPosLapin;
       private float yPosLapin;
      
       private int type_animation;
       private boolean animation_stop;

       private int type_application;
      
       @Override
       public void create() {
      
           // Type d'application
           ApplicationType type = Gdx.app.getType();
           type_application= type.compareTo(ApplicationType.Desktop);
         
           // Si Application Android Augmenter la vitesse du lapin
           if(type_application!=0)
                vitesse_lapin*=10;         
      
             // Initialisation  
           batch = new SpriteBatch();
           sheetTexture = new Texture(Gdx.files.internal("rabbitmanSheet.PNG"));
           largeur_texture = sheetTexture.getWidth();
           hauteur_texture = sheetTexture.getHeight();
           animation_stop = false;
           animationLapin = new Animation[8] ;
           type_animation=0;
           temps=0.0f;

           // Construction du tableau d'images constituants l'animation
           TextureRegion[][] tmp = TextureRegion.split(sheetTexture, largeur_texture/nb_colone, hauteur_texture/nb_ligne);
           regionInitial = tmp[0][1];
          
           // Positionner le Lapin en milieu
           xPosLapin= Gdx.graphics.getWidth()*0.5f;
          yPosLapin= Gdx.graphics.getHeight()*0.5f;
       
          // Instancier l'animation
          for(int i=0;i<8;i++)
          animationLapin[i] = new Animation(duree_animation/nombre_image_animation, tmp[i]);
       
      }
      
       @Override
       public void dispose() {
       }

       @Override
       public void pause() {
       }

       @Override
       public void render() {
            
         Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);   
         Gdx.gl.glClearColor(0,0.5f,0.2f,0);
   
        
         // Fixer lapin
         if(!animation_stop) // si aucune manipulation en cours
         {
         temps += Gdx.graphics.getDeltaTime();                   
         regionCourante = animationLapin[type_animation].getKeyFrame(temps/1, true);  
         }
        
         // Dessiner Lapin
         batch.begin();
         batch.draw(regionCourante, xPosLapin, yPosLapin);                    
         batch.end();
      
         // Manipuler Lapin
         if(type_application == 0)  // application Desktop
         ManipulerLapin_clavier();
         if(type_application == -1)  // application Android
         ManipulerLapin_accelerometre();
       }

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

//*********************** manipulation entrées accéléromètre******************************//
       private void ManipulerLapin_accelerometre() // => destinée Android
       {
             // Les directions principales
             if(Gdx.input.getAccelerometerY()>1)
             {
                    xPosLapin+=vitesse_lapin;
                    type_animation=7;
             }
             if(Gdx.input.getAccelerometerY()<-1)
             {
                    xPosLapin-=vitesse_lapin;
                    type_animation=3;
             }
             if(Gdx.input.getAccelerometerX()>1)
             {
                    yPosLapin-=vitesse_lapin;
                    type_animation=0;
             }
             if(Gdx.input.getAccelerometerX()<-1)
             {
                    yPosLapin+=vitesse_lapin;
                    type_animation=4;
             }
            
             // La direction initial du Lapin
             if(Gdx.input.getAccelerometerX()<1 && Gdx.input.getAccelerometerY()<1 && Gdx.input.getAccelerometerX()>-1 && Gdx.input.getAccelerometerY()>-1 )
             {
                    animation_stop = true;
                    regionCourante = regionInitial ;
             }else
             {
                    animation_stop = false;
             }
       }
      
//****************** Manipulation entrées accéléromètre***********************************//
      
       private void ManipulerLapin_clavier() // => Destinée pour Desktop
       {
            
             // Les directions principales
            
             if(Gdx.input.isKeyPressed(Keys.UP))
             {
                    yPosLapin+=vitesse_lapin;
                    type_animation=4;
             }
             if(Gdx.input.isKeyPressed(Keys.DOWN))
             {
                    yPosLapin-=vitesse_lapin;
                    type_animation=0;
             }
             if(Gdx.input.isKeyPressed(Keys.RIGHT) )
             {
                    xPosLapin+=vitesse_lapin;
                    type_animation=7;
             }
             if(Gdx.input.isKeyPressed(Keys.LEFT) )
             {
                    xPosLapin-=vitesse_lapin;
                    type_animation=3;
             }
            
             // Les directions secondaires
             if(Gdx.input.isKeyPressed(Keys.UP) && Gdx.input.isKeyPressed(Keys.RIGHT) )
             {
                    yPosLapin+=vitesse_lapin*0.5f;
                    xPosLapin+=vitesse_lapin*0.5f;
                    type_animation=2;
             }
             if(Gdx.input.isKeyPressed(Keys.UP) && Gdx.input.isKeyPressed(Keys.LEFT))
             {
                    yPosLapin+=vitesse_lapin*0.5f;
                    xPosLapin-=vitesse_lapin*0.5f;
                    type_animation=6;
             }
             if(Gdx.input.isKeyPressed(Keys.DOWN) && Gdx.input.isKeyPressed(Keys.RIGHT))
             {
                    yPosLapin-=vitesse_lapin*0.5f;
                    xPosLapin+=vitesse_lapin*0.5f;
                    type_animation=5;
             }
             if(Gdx.input.isKeyPressed(Keys.DOWN) && Gdx.input.isKeyPressed(Keys.LEFT))
             {
                    yPosLapin-=vitesse_lapin*0.5f;
                    xPosLapin-=vitesse_lapin*0.5f;
                    type_animation=1;
             }
            
             // La direction initial du Lapin
             if(!Gdx.input.isKeyPressed(Keys.ANY_KEY))
             {
                    regionCourante = regionInitial;
                    animation_stop=true;
             }
             else
                    animation_stop=false;
       }
}

* Voici résultat sous Desktop :
Bien sûr, il faut la classe lanceur pour lancer l’application sous Desktop voir précédant tutoriel





* Voici le résultat sous Android :
Bien sûr, il faut la classe main pour lancer l’application sous Android voir précédant tutoriel




Pour télécharger les ressources :
RabbitManSheet.png
sumoSheet.png

Conclusion                     
                                      
Ainsi s’achève le tutoriel de l’animation avec Libgdx, sachez qu’il existe d’autre façon de faire une animation, mais utiliser la classe Animation est le plus pratique des moyens.
Si vous avez des remarques ou des questions n’hésitez pas à les poster en commentaire. Merci pour votre lecture.

4 commentaires: