A small sprite animation framework

A small sprite animation framework

Introduction

Now that we have presented the principle of sprite extraction (sprites as sub-images of a single composite image), let’s write a small sprite animation framework.

Here is how you would create and animate a sprite:

  1. var robot;
  2. window.onload = function() {
  3.    canvas = document.getElementById(“canvas”);
  4.    ctx = canvas.getContext(“2d”);
  5.   // Load the spritesheet
  6.   spritesheet = new Image();
  7.   spritesheet.src = SPRITESHEET_URL;
  8.   // Called when the spritesheet has been loaded
  9.   spritesheet.onload = function() {
  10.     …
  11.     robot = new Sprite();
  12.     // 1 is the posture number in the sprite sheet. We have
  13.     // only one with the robot.
  14.     robot.extractSprites(spritesheet, NB_POSTURES, 1,
  15.                          NB_FRAMES_PER_POSTURE,
  16.                          SPRITE_WIDTH, SPRITE_HEIGHT);
  17.     robot.setNbImagesPerSecond(20);
  18.     requestAnimationFrame(mainloop);
  19.  }; // onload
  20. };
  21. function mainloop() {
  22.   // Clear the canvas
  23.   ctx.clearRect(0, 0, canvas.width, canvas.height);
  24.   // draw sprite at 0, 0 in the small canvas
  25.   robot.draw(ctx, 0, 0, 1);
  26.   requestAnimationFrame(mainloop);
  27. }

Try the example on JSBin that uses this framework first! Experiment by editing line 20robot.setNbImagesPerSecond(20); changing the value of the parameter and observing the result.

Example of the sprite framework on JsBin. Screenshot

THE SPRITEIMAGE object AND SPRITE MODELS

In this small framework we use “SpriteImage “, a JS object we build to  represent one sprite image. Its properties are: the global sprite sheet to which it belongs, its position in the sprite sheet, and its size. It also has a draw method for drawing the sprite image at an xPos, yPos position, and at anappropriate size. 

  1. function SpriteImage(img, x, y, width, height) {
  2.    this.img = img; // the whole image that contains all sprites
  3.    this.= x;     // x, y position of the sprite image in the whole image
  4.    this.= y;
  5.    this.width = width; // width and height of the sprite image
  6.    this.height = height;
  7.   
  8.    this.draw = function(ctx, xPos, yPos, scale) {
  9.       ctx.drawImage(this.img,
  10.         this.x, this.y, // x, y, width and height of img to extract
  11.         this.width, this.height,
  12.         xPos, yPos, // x, y, width and height of img to draw
  13.         this.width*scale, this.height*scale);
  14.  };
  15. }

We define the Sprite model. This is the one we used to create the small robot in the previous example.

    • A Sprite is defined by an array of SpriteImage objects.
    • It has a method for extracting all SpriteImages from a given sprite sheet and filling the above array.
    • It has a draw method which will draw the current SpriteImage. A Sprite is an animated object, therefore, calling draw multiple times will involve an automatic change of the current SpriteImage being drawn.
    • The number of different images to be drawn per second is a parameter of the sprite.

Here is the code of the Sprite model:

  1. function Sprite() {
  2.   this.spriteArray = [];
  3.   this.currentFrame = 0;
  4.   this.delayBetweenFrames = 10;
  5.   this.extractSprites = function(spritesheet,
  6.                                  nbPostures, postureToExtract,
  7.                                  nbFramesPerPosture,
  8.                                  spriteWidth, spriteHeight) {
  9.     // number of sprites per row in the spritesheet
  10.     var nbSpritesPerRow = Math.floor(spritesheet.width / spriteWidth);
  11.     // Extract each sprite
  12.     var startIndex = (postureToExtract -1) * nbFramesPerPosture;
  13.     var endIndex = startIndex + nbFramesPerPosture;
  14.     for(var index = startIndex; index < maxIndex; index++) {
  15.     // Computation of the x and y position that corresponds to the sprite
  16.       // index
  17.       // x is the rest of index/nbSpritesPerRow * width of a sprite
  18.       var x = (index % nbSpritesPerRow) * spriteWidth;
  19.       // y is the divisor of index by nbSpritesPerRow * height of a sprite
  20.       var y = Math.floor(index / nbSpritesPerRow) * spriteHeight;
  21.       // build a spriteImage object
  22.       var s = new SpriteImage(spritesheet, x, y, spriteWidth, spriteHeight);
  23.       this.spriteArray.push(s);
  24.     }
  25.   };
  26.   this.then = performance.now();
  27.   this.totalTimeSinceLastRedraw = 0;
  28.   this.draw = function(ctx, x, y) {
  29.     // Use time based animation to draw only a few images per second
  30.     var now = performance.now();
  31.     var delta = now  this.then;
  32.     // Draw currentSpriteImage
  33.     var currentSpriteImage = this.spriteArray[this.currentFrame];
  34.     // x, y, scale. 1 = size unchanged
  35.     currentSpriteImage.draw(ctx, x, y, 1);
  36.     // if the delay between images is elapsed, go to the next one
  37.     if (this.totalTimeSinceLastRedraw > this.delayBetweenFrames) {
  38.     // Go to the next sprite image
  39.       this.currentFrame++;
  40.       this.currentFrame %= this.spriteArray.length;
  41.     // reset the total time since last image has been drawn
  42.       this.totalTimeSinceLastRedraw = 0;
  43.     } else {
  44.       // sum the total time since last redraw
  45.       this. totalTimeSinceLastRedraw += delta;
  46.     }
  47.     this.then = now;
  48.   };
  49.   this.setNbImagesPerSecond = function(nb) {
  50.     // delay in ms between images
  51.     this.delayBetweenFrames = 1000 / nb;
  52.   };
  53. }

Same example but with the walking woman sprite sheet

Try this JsBin

Woman animated

This time we have changed the parameters of the sprites and sprite sheet. Now you can select the index of the posture to extract: the woman sprite sheet has 8 different postures, so you can call:

  1. womanDown.extractSprites(spritesheet, NB_POSTURES, 1,
  2.                          NB_FRAMES_PER_POSTURE,
  3.                          SPRITE_WIDTH, SPRITE_HEIGHT);
  4. womanDiagonalBottomLeft.extractSprites(spritesheet, NB_POSTURES, 2,
  5.                         NB_FRAMES_PER_POSTURE,
  6.                         SPRITE_WIDTH, SPRITE_HEIGHT);
  7. womanLeft.extractSprites(spritesheet, NB_POSTURES, 3,
  8.                          NB_FRAMES_PER_POSTURE,
  9.                          SPRITE_WIDTH, SPRITE_HEIGHT);
  10. // etc…

Moving the sprites, stopping the sprites

Example at JsBin

Woman moving left and right, jsbin screenshot

As usual, we used key listeners, an inputStates global object, and this time we created 8 woman sprites, one for each direction.

Notice that we added a drawStopped method in the Sprite model in order to stop animating the woman when no key is pressed for moving her.

Leave a comment