Dans les jeux 2D, un sprite est un élément qui peut se
déplacer, s’animer. Il possède théoriquement une certaine couleur de transparence
ou bien carrément un canal alpha. En
fait il existe bien différentes manières de faire une animation: soit en
faisant défiler des séquences successives d’images comme dans les gifs animés, soit en faisant des transformations
squelettiques sur un objet.
Dans ce tutoriel, nous allons remplacer notre image du
premier tutoriel par un personnage qui est animée en fonction de sa direction.
Le problème survient ici c’est que le sprite est toujours animé à la cadence du FPS. C’est-à-dire si on augmente le FPS (nombre de frames par seondes), on vera le personnage se déplacer plus vite. Il faut chercher à isoler la rapidité des sprites avec la boucle du jeu.
Ce que l’on peut faire c’est de fournir à chaque update le temps écoulé depuis le dernier frame, par exemple GameLoop.update(long elapsedTime).
Ainsi on a la possibilité par exemple de dire au sprite de changer de direction après exactement 3 secondes.
Téléchargements
Le corps du jeu (GameLoop)
Par rapport au premier tutoriel, on a juste pris en considération le sprite: création avec mario=new Sprite(...), la mis à jour avec mario.update() puis l'affichage avec mario.render(screen.canvas)
Par rapport au premier tutoriel, on a juste pris en considération le sprite: création avec mario=new Sprite(...), la mis à jour avec mario.update() puis l'affichage avec mario.render(screen.canvas)
package com.creativegames; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Paint; import android.graphics.drawable.BitmapDrawable; import android.view.MotionEvent; public class GameLoop extends Thread { /** Variable booléenne pour arrêter le jeu */ public boolean running; /** durée de la pause entre chaque frame (FPS=10) */ private long sleepTime = 100; /** Ecran de jeu */ public GameView screen; /** le dernier évenement enregistré */ public MotionEvent lastEvent; /** Personnage animé */ private Sprite mario; /** Valeur du FPS (nombre de frames par secondes) */ private int fps; public void initGame(Context context) { // créer la vue du jeu this.screen = new GameView(context, this); Bitmap img = ((BitmapDrawable) context.getResources().getDrawable( R.drawable.bad1)).getBitmap(); // créer un sprite en spécifiant la position et la vitesse puis l'écran mario = new Sprite(img, 0, 0, 2, 3, screen); running = true; } /** * Ici on va faire en sorte que lorsqu'on clique sur l'écran, le sprite va * se rendre à la position cliquée * */ public void processEvents() { if (lastEvent != null && lastEvent.getAction() == MotionEvent.ACTION_DOWN) { int xTouch = (int)lastEvent.getX(); int yTouch = (int)lastEvent.getY(); mario.moveTo(xTouch, yTouch); } lastEvent = null; } /** * Mise à jour des composants du jeu Ici nous déplaçon le personnage avec la * vitesse vx S'il sort de l'écran, on le fait changer de direction * */ public void update() { mario.update(); } /** Dessiner les composant du jeu sur le buffer de l'écran */ public void render() { Paint paint = new Paint(); // effacer l'écran avec la couleur verte paint.setColor(0xFF008000); this.screen.canvas.drawPaint(paint); // dessiner le sprite this.mario.render(screen.canvas); // ecrire le fps paint.setColor(0xFFFFFFFF); this.screen.canvas.drawText("FPS : " + fps, 10, 10, paint); // appliquer le buffer à l'écran this.screen.invalidate(); } @Override public void run() { long startTime; long elapsedTime; // durée de (update()+render()) long sleepCorrected; // sleeptime corrigé while (this.running) { startTime = System.currentTimeMillis(); this.processEvents(); this.update(); this.render(); elapsedTime = System.currentTimeMillis() - startTime; sleepCorrected = sleepTime - elapsedTime; // si jamais sleepCorrected<0 alors faire une pause de 1 ms if (sleepCorrected < 0) { sleepCorrected = 1; } try { Thread.sleep(sleepCorrected > 0 ? sleepCorrected : 1); } catch (InterruptedException e) { } // calculer le FSP fps = (int) (1000/(System.currentTimeMillis() - startTime)); } } }
La gestion des animations
Notre sprite est une image qui contient 4 animations avec 3
frames chacunes.
Alors, quand on observe notre image, on a les correspondances
lignes-animation suivantes (ligne3=up,
ligne1=left, ligne0=down, ligne2=right)
Maitenant si la vitesse en x est supérieure en valeur
absolue à la vitesse en y, on considèrera que le personnage se déplace soit à
gauche ou à droite. Dans le cas contraire, on considèrera qu’il se déplace soit
vers le haut ou vers le bas.
Nous
avons donc besoin d’une fonction qui retourne l’animation à utiliser en
fonction des paramètres de vitesse (xSpeed, ySpeed).
private int getAnimationRow() { // animation: 3 back, 1 left, 0 front, 2 right if (Math.abs(xSpeed) > Math.abs(ySpeed)) { if (xSpeed > 0) return 2; //right else return 1; //left } else { if(ySpeed>0) return 0; //down else return 3; //up } }
Maintenant pour sélectionner
un morceau d’image et l’afficher, on utilise la fonction.
public void render(Canvas canvas) { int srcX = currentFrame * width; int srcY = getAnimationRow() * height; Rect src = new Rect(srcX, srcY, srcX + width, srcY + height); Rect dst = new Rect(x, y, x + width, y + height); canvas.drawBitmap(image, src, dst, null); }Enfin le code source intégral de la classe Sprite
package com.creativegames; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; public class Sprite { private static final int BMP_ROWS = 4; private static final int BMP_COLUMNS = 3; private GameView gameView; private Bitmap image; private int x; private int y; private int xSpeed; private int ySpeed; /** numéro de la frame courante */ private int currentFrame; /** dimension du sprite */ private int width; private int height; public Sprite(Bitmap bmp, int x, int y, int vx, int vy, GameView gameView) { this.width = bmp.getWidth() / BMP_COLUMNS; this.height = bmp.getHeight() / BMP_ROWS; this.gameView = gameView; this.image = bmp; this.x = x; this.y = y; this.xSpeed = vx; this.ySpeed = vy; } public void update() { if (x >= gameView.getWidth() - width - xSpeed || x + xSpeed <= 0) { xSpeed = -xSpeed; } x = x + xSpeed; if (y >= gameView.getHeight() - height - ySpeed || y + ySpeed <= 0) { ySpeed = -ySpeed; } y = y + ySpeed; currentFrame = (currentFrame + 1) % BMP_COLUMNS; } private int getAnimationRow() { // animation: 3 back, 1 left, 0 front, 2 right if (Math.abs(xSpeed) > Math.abs(ySpeed)) { if (xSpeed > 0) return 2; //right else return 1; //left } else { if(ySpeed>0) return 0; //down else return 3; //up } } public void render(Canvas canvas) { int srcX = currentFrame * width; int srcY = getAnimationRow() * height; Rect src = new Rect(srcX, srcY, srcX + width, srcY + height); Rect dst = new Rect(x, y, x + width, y + height); canvas.drawBitmap(image, src, dst, null); } /** déterminer la vitesse vers le point cliqué */ public void moveTo(int x2, int y2) { int dx = x2-this.x; int dy = y2-this.y; double r = Math.sqrt(dx*dx+dy*dy); xSpeed = (int) (3*dx/r); ySpeed = (int) (3*dy/r); } }Inconvénients de cette méthode !
Le problème survient ici c’est que le sprite est toujours animé à la cadence du FPS. C’est-à-dire si on augmente le FPS (nombre de frames par seondes), on vera le personnage se déplacer plus vite. Il faut chercher à isoler la rapidité des sprites avec la boucle du jeu.
Ce que l’on peut faire c’est de fournir à chaque update le temps écoulé depuis le dernier frame, par exemple GameLoop.update(long elapsedTime).
Ainsi on a la possibilité par exemple de dire au sprite de changer de direction après exactement 3 secondes.
Téléchargements
Aucun commentaire:
Enregistrer un commentaire