Animer un sprite javascript html5

 

Cet article fait suite à Coder un sprite javascript html5 en y ajoutant cette fois l’animation du sprite comme par exemple les battements d’ailes d’un oiseau ou encore la marche à pieds.

 

L’animation

Voici l’animation à réaliser.

 

Le détail de l’animation

Voici le détail des mouvements de l’animation

Tous ces mouvements constituent un seul et unique fichier image de 108 pixels sur 46 qui va servir à produire l’animation. Il y a six mouvements répartis sur les 108 pixels, chaque mouvement faisant une taille de 18 pixels de large sur 46 de haut.

La taille de 18 pixels est obtenue en divisant la largeur du fichier image par le nombre total d’images (108/6=18).

Pour produire l’animation, les images doivent se succéder les unes aux autres dans l’ordre de gauche à droite. Lorsque la dernière image est affichée, il faut reprendre l’animation depuis le début. Tout ceci en boucle.

Il faut donc découper la bonne image dans le ficher image avant de l’afficher. Les images étant placées dans le bon ordre d’affichage dans ce même fichier, la tâche en est facilitée.

Pour résumer, vous n’affichez pas toute l’image mais seulement une coupe, celle relative à l’image de l’animation puis vous passez à la coupe suivante: 1, puis 2, puis 3, puis 4, puis 5, puis 6, puis 1, puis 2, etc… en boucle.

 

Le canvas html5

Pour l’exercice, vous initialisez un canvas html5 dans une page html.
Et vous récupérer ce même canvas html5 depuis javascript.

<html>
 <body>
  <canvas id="spritedemo" style="border:1px solid black;width:1600px;height:1200px;" width="800" height="600"></canvas>
 </body>
<script>
{
let canvasGameContext = document.getElementById("spritedemo").getContext("2d");
}
</script>

Ce canvas html5 de 800 sur 600 pixels sera le support d’affichage du sprite.

 

Le code existant

Vous allez repartir du code existant sur le sprite javascript> en y ajoutant tout le nécessaire pour que l’animation prenne vie et en gardant la compatibilité avec ce que vous auriez créé avant de mettre à jour cet objet javascript.

var game = {};
game.display = {
 sprite : {
  image : null,
  width : null,
  height : null,
  posX : null,
  posY : null,

  setSize : function(width, height) {
   this.width = width;
   this.height = height;
  },

  render : function (canvasContext, x, y) {
   this.posX = x;
   this.posY = y;
   canvasContext.drawImage(this.image, this.posX, this.posY);
  }
 },

 createSprite : function(imagePath) {
  var sprite = Object.create(this.sprite);
  sprite.image = new Image();
  sprite.image.src = imagePath;
  return sprite;
 }
}

 

Ce qui évolue

La méthode render affiche le sprite dans le canvas html5 par le biais de la fonction drawImage. C’est donc ici qu’il faut modifier le code existant pour sélectionner et afficher la partie coupée qui correspond à une image de l’animation.

Par chance, la fonction drawImage a plusieurs signatures dont l’une permet de couper et afficher une partie d’une image passée en paramètre.

La signature

context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);

Où :
img : l’image;
sx et sy : les coordonnées du point à partir duquel l’image sera coupée;
swidth et sheight : la largeur et la hauteur de la coupe en pixels;
x et y : la position de la coupe dans le canvas html5;
width et height : la hauteur et la largeur de l’image (la coupe) affichée dans le canvas html5.

Pour les deux derniers paramètres, si les valeurs sont supérieures aux valeurs de la coupe, l’image sera étirée.
Inversement si les valeurs sont inférieures aux valeurs de la coupe, l’image sera réduite.

Au préalable, vous devez stocker le nombre d’images qui composent l’animation dans une variable dédiée numberOfFrames ainsi que le numéro de l’image frameIndex qui est affichée :

var game = {};
game.display = {
 sprite : {
  image : null,
  width : null,
  height : null,
  posX : null,
  posY : null,
  numberOfFrames : null,
  frameIndex : 0,
  .....
 }
}

La propriété frameIndex est une variable à usage interne, elle n’est pas exposée ou manipulée par l’utilisateur de la classe. Son rôle est de stocker le numéro de l’image affichée.

L’ajout de la propriété numberOfFrames nécessite de modifier la fonction createSprite :

 createSprite : function(imagePath, numberOfFrames) {
  var sprite = Object.create(this.sprite);
  sprite.image = new Image();
  sprite.image.src = imagePath;
  sprite.numberOfFrames = numberOfFrames;
  return sprite;
 }

Pour faire les coupes d’images successives, il faut faire varier sx et sy. Dans le cas présent, uniquement sx puisque toutes les images sont placées horizontalement les unes derrières les autres. sy prend uniquement la valeur 0.

La valeur de sx peut se déduire du numéro d’image ou plutôt du numéro d’image moins 1 (this.frameIndex) multiplié par la largeur de l’image (this.width / this.numberOfFrames):

Pour ce qui est de la taille de la coupe, elle est toujours la même :
– la taille totale de l’image (width) divisée par le nombre d’images (numberOfFrames) donne la largeur de la coupe;
– la hauteur de la coupe est toujours la même puisque toutes les images sont placées horizontalement les unes derrières les autres.

Et c’est la même chose pour la largeur et la hauteur de l’image affichée.

Les paramètres d’appel de la fonction drawImage deviennent :

canvasContext.drawImage(
 this.image,  // l'image 
 this.frameIndex * this.width / this.numberOfFrames, // coordonnées x du début de la coupe
 0, // coordonnées y du début de la coupe
 this.width / this.numberOfFrames, // largeur de la coupe
 this.height, // hauteur de la coupe
 this.posX, // coordonnées x du sprite animé
 this.posY, // coordonnées y du sprite animé
 this.width / this.numberOfFrames, // largeur de l'image affichée
 this.height // hauteur de l'image affichée
);

Pour que l’animation prenne forme, il faut appeler la fonction render de manière continue. Pour l’exemple, je vous propose de faire simple en utilisant la fonction javascript setInterval qui prend en paramètres une fonction et une fréquence d’appel en millisecondes.

let mySprite = game.display.createSprite("walk.png", 6);
mySprite.setSize(108,46);
setInterval(function() {
  mySprite.render(canvasGameContext,0,0);
}, 50);

En l’état, il n’y a pas d’animation, seule la première image est affichée : sx ne varie pas. C’est là qu’entre en jeu la propriété frameIndex: en la faisant varier de 0 à 5, vous faites varier sx aussi.

Ajoutez une nouvelle fonction update à la classe sprite qui ne fait qu’incrémenter de 1 le frameIndex et boucler sur les valeurs de 0 à 5.

let game = {};
game.display = {
 sprite : {
  image : null,
  width : null,
  height : null,
  posX : null,
  posY : null,
  numberOfFrames : null,
  frameIndex : 0,
  .....
  update : function() {
   this.frameIndex = (this.frameIndex+1)%6;
  },
}

let mySprite = game.display.createSprite("walk.png", 6);
mySprite.setSize(108,46);
setInterval(function() {
 mySprite.update();
 mySprite.render(canvasGameContext,0,0);
}, 50);

Là ça s’anime, mais les images se superposent les unes aux autres d’où la nécessité d’effacer le canvas html5 avant tout affichage d’une image.

let mySprite = game.display.createSprite("walk.png", 6);
mySprite.setSize(108,46);
setInterval(function() {
 mySprite.update();
 canvasGameContext.clearRect(0,0,800,600);
 mySprite.render(canvasGameContext,0,0);
}, 50);

Le travail est terminé.

Simple information, l’image n’ayant pas le temps de se charger en mémoire, votre sprite html5 pourrait ne pas s’afficher. Je vous invite donc à procéder au chargement des images avant d’afficher un sprite html5. L’article Loader d’images en javascript vous montre comment faire.

 

Si vous souhaitez obtenir le code source, faites un commentaire. Je me ferai une joie de vous l’envoyer personnellement.
Faites vos remarques ou propositions d’améliorations. Si cet article vous a plu ou aidé, faites le savoir par un commentaire ou sur les réseaux sociaux.

 

Posté dans javascript / html5Tagged HTML5, html5 javascript, javascript, sprites, sprites html5 js  |  Laisser un commentaire

Répondre