Вы находитесь на странице: 1из 10

An Introduction to Spritesheet Animation

by Steven Lambert26 Nov 2013


Difficulty:BeginnerLength:MediumLanguages:
SpritesheetsProgrammingGame Art2D GamesSide ScrollerJavaScriptPlatform
AgnosticHTMLHTML5Infinite Runner

Spritesheets have been used in games for a long time. Classic games such as Legend of Zelda:
A Link to the Past and even modern games like Cut the Rope have used them. In this article,
we'll talk about what spritesheet animation is and how to code it, and we'll also demonstrate
how they can be used in a small game. I'll be using JavaScript for the code, but you should be
able to follow along in any language.

Related Posts

This tutorial is part of a special collaboration between an artist, an animator and a gamedev!

• Art by Mary Winkler


• Animation by Adam Everett Miller
• Gamedev by Steven Lambert

Hit Space to jump.

Before we can begin talking about how to code a spritesheet animation, we should first define
a few terms: animation, sprite, and spritesheet.

Related Posts

Additional resources you may find useful:

• Animating With Asset Sheets: An Alternative to Blitting


• 10 Great Full Game Sprite Sheets From GraphicRiver
• Creating Sprite Sheets in Five Minutes With Texture Packer

Animation
Back in 1872, Eadweard Muybridge was commissioned to prove whether a horse lifted all
four legs off the ground at once when it ran. To do so, he set up a series of cameras along a
track and took pictures in quick succession as a horse ran by. This process allowed him to
capture 16 pictures of the horse's run. In one of the pictures, the horse did indeed have all four
legs off the ground.
Muybridge's "The Horse in Motion" photos with the first photo removed.

Muybridge later repeated the experiment and placed each photo onto a device that could
project the photos in rapid succession to give the illusion of the horse running, creating the
first movie projector.

The process of changing images in quick succession to give the illusion of movement is called
animation.

Sprite
A sprite is a single graphic image that is incorporated into a larger scene so that it appears to
be part of the scene.
Sprites are a popular way to create large, complex scenes as you can manipulate each sprite
separately from the rest of the scene. This allows for greater control over how the scene is
rendered, as well as over how the players can interact with the scene.

It is not uncommon for games to have tens to hundreds of sprites. Loading each of these as an
individual image would consume a lot of memory and processing power. To help manage
sprites and avoid using so many images, many games use spritesheets.

If you're looking for pre-made, creative graphics, discover Game Sprites and Sheets that may
be just the right fit for your needs. Or you could order your own customized game character
sprite on Envato Studio.

Spritesheet
When you put many sprites into a single image, you get a spritesheet.

Spritesheets are used to speed up the process of displaying images to the screen; It is much
faster to fetch one image and display only a part of that image than it is to fetch many images
and display them.

Spritesheet animation is nothing more than taking a spritesheet and changing which sprite is
rendered in quick succession to give the illusion of movement, much like a film projector
displaying a movie.

Parts of a Spritesheet
Spritesheets are made up of two parts: frames and cycles

A frame is a single image (or sprite) from the spritesheet. Going back to the Muybridge's
horse example, each picture of the horse in the image would be a frame.

When the frames are put in an order that creates a continuous movement, it creates a cycle.

Putting the photos of the horse in the order that they were taken produces a "run" cycle since
the horse is running (as opposed to a "walk" or "idle" cycle).
Coding Spritesheet Animations
There are three parts to coding a spritesheet animation:

1. Creating the image


2. Updating the image to each frame of the animation
3. Drawing the frame to the screen

Creating the Image

We'll start by creating the function (or class) that will handle our spritesheet animation. This
function will create the image and set its path so that we can use it to draw.

01
02 function SpriteSheet(path, frameWidth, frameHeight) {
03
var image = new Image();
04 var framesPerRow;
05
06 // calculate the number of frames in a row after the image loads
07 var self = this;
08 image.onload = function() {
framesPerRow = Math.floor(image.width / frameWidth);
09 };
10
11 image.src = path;
12 }
13

Since different spritesheets can have different frame sizes, we'll need to pass the frame width
and height so that we can accurately calculate how many frames are in a row and column of
the image. We'll use this information later to draw the animation to the screen.

It is important that each frame of the spritesheet is the same width and height; otherwise,
drawing the animation to the screen is very difficult.

Updating the Image

To update the spritesheet animation, all we have to do is change which frame we will draw.
Below is the spritesheet divided into each of its frames and numbered.
At every frame of the game, we'll update the spritesheet. However, we don't want the
animation to switch to the next frame every frame, so we need to tell our spritesheet how
many frames to wait before transitioning.

It is important to note that not every spritesheet has a sprite in every available frame (such as
the image of Muybridge's "The Horse in Motion"). If we were to try to animate our spritesheet
with an empty frame, there would be a blip in the animation every time the blank frame is
drawn to the screen.

To compensate for this, we will also tell the spritesheet what the last frame number is, so that
we don't animate empty frames.

01 function SpriteSheet(path, frameWidth, frameHeight, frameSpeed, endFrame)


02 {
03
// code removed for brevity
04
05 var currentFrame = 0; // the current frame to draw
06 var counter = 0; // keep track of frame rate
07
08 // Update the animation
09 this.update = function() {
10
11 // update to the next frame if it is time
if (counter == (frameSpeed - 1))
12 currentFrame = (currentFrame + 1) % endFrame;
13
14 // update the counter
15 counter = (counter + 1) % frameSpeed;
16 }
17 };
18

By using the modulo operator (%) for the currentFrame, we can create a continuous loop—
every time the endFrame is reached, the currentFrame will revert back to 0, thus looping the
animation.

The modulo operator for the counter prevents integer overflow.

Drawing the Image

Drawing an image from a spritesheet works in exactly the same way as drawing an image
from a tile map.

We calculate the row of the image we want to draw by taking the modulo of the current frame
and the number of frames per row. We calculate the column by dividing the current frame by
the number of frames per row.

Using this row and column, we can then calculate the coordinates of the frame to draw by
multiplying them by frameWidth and frameHeight, respectively:

01
02 // Draw the current frame
03 this.draw = function(x, y) {
// get the row and col of the frame
04 var row = Math.floor(currentFrame / framesPerRow);
05 var col = Math.floor(currentFrame % framesPerRow);
06
07 ctx.drawImage(
08 image,
09 col * frameWidth, row * frameHeight,
frameWidth, frameHeight,
10 x, y,
11 frameWidth, frameHeight);
12 };
13 }
14

With the spritesheet function in place, we can now use it to create a spritesheet animation:

01 spritesheet = new SpriteSheet('Walk_Cycle_Image.png', 125, 125, 3, 16);


02
function animate() {
03 requestAnimFrame( animate );
04 ctx.clearRect(0, 0, 150, 150);
05
06 spritesheet.update();
07
08 spritesheet.draw(12.5, 12.5);
09 }
10

Multiple Cycles in One Spritesheet


The above code will work for any spritesheet containing one cycle. However, it is not
uncommon for a spritesheet to hold multiple cycles, meaning that there will be multiple
animations in a single spritesheet.

We will need to change how our spritesheet works in order to handle multiple animations
from a single spritesheet.

Creating the Image

Since the image remains the same between animations, we will divide our spritesheet into two
functions: one for the image and one for each animation from the spritesheet.

A spritesheet will hold the information about the image and the frame sizes.

01
02 function SpriteSheet(path, frameWidth, frameHeight) {
this.image = new Image();
03 this.frameWidth = frameWidth;
04 this.frameHeight = frameHeight;
05
06 // calculate the number of frames in a row after the image loads
07 var self = this;
08 this.image.onload = function() {
self.framesPerRow = Math.floor(self.image.width / self.frameWidth);
09 };
10
11 this.image.src = path;
12 }
13

Updating and Drawing the Image

An animation will be in charge of updating and drawing the spritesheet.

function Animation(spritesheet, frameSpeed, startFrame, endFrame) {


01
02 var animationSequence = []; // array holding the order of the animation
03 var currentFrame = 0; // the current frame to draw
04 var counter = 0; // keep track of frame rate
05
06 // create the sequence of frame numbers for the animation
07 for (var frameNumber = startFrame; frameNumber <= endFrame;
frameNumber++)
08 animationSequence.push(frameNumber);
09
10 // Update the animation
11 this.update = function() {
12
// update to the next frame if it is time
13 if (counter == (frameSpeed - 1))
14 currentFrame = (currentFrame + 1) % animationSequence.length;
15
16 // update the counter
17 counter = (counter + 1) % frameSpeed;
18 };
19
// draw the current frame
20 this.draw = function(x, y) {
21 // get the row and col of the frame
22 var row = Math.floor(animationSequence[currentFrame] /
23 spritesheet.framesPerRow);
24 var col = Math.floor(animationSequence[currentFrame] %
spritesheet.framesPerRow);
25
26 ctx.drawImage(
27 spritesheet.image,
28 col * spritesheet.frameWidth, row * spritesheet.frameHeight,
29 spritesheet.frameWidth, spritesheet.frameHeight,
x, y,
30 spritesheet.frameWidth, spritesheet.frameHeight);
31 };
32 }
33
34 spritesheet = new SpriteSheet('Walk_Cycle_Image.png', 125, 125);
35 walk = new Animation(spritesheet, 3, 0, 15);
36
function animate() {
37 requestAnimFrame( animate );
38 ctx.clearRect(0, 0, 150, 150);
39
40 walk.update();
41
42 walk.draw(12.5, 12.5);
}
43
44
45
46
47

Since the spritesheet contains more frames than any single animation will need, we'll need to
know which frame number to start and end the animation. Using this information, we'll create
an array of frame numbers so that we can use currentFrame to access the correct frame
number.

Advertisement

Putting the Spritesheet to Use


With the animation ready to handle any spritesheet, we can use it to make a simple Canabalt-
style infinite runner:
Hit Space to jump.

You can find the full source code for this in our GitHub repo. What's your high score?

Extra Resources
If you're looking for creative game graphics, we have affordable Game Sprites and Sheets on
GraphicRiver, which may be just the solution your game needs. Or, if you're interested in
getting some help with your animations, Envato Studio has a great collection
of Animators that you might like to explore.

Вам также может понравиться