Skip to content
Lesson 9 of 14

Step 1 of 5 · Reading · ~3 min

Read

Resources & Rendering

Sprites & Rendering

A 2D game draws pictures cut out of one large image, picks the right cut by looking at a clock, and draws them back to front. After this lesson your program will do the bookkeeping behind that: for every sprite it works out which rectangle of the sprite sheet is on screen right now, and it lists the sprites in the order they must be drawn.

Sprite sheets

Loading a separate image file per frame would force the GPU to switch textures constantly. Artists instead pack many frames into one sprite sheet (a texture atlas) laid out as a grid of equal cells. A frame is just a number. In a sheet that is cols cells wide, with cells cw by ch pixels, frames are numbered left to right and then top to bottom, so frame f sits in column f mod cols and row f div cols, and its source rectangle is

x = column * cw     y = row * ch     size = cw by ch

With 4 columns of 16x16 cells, frame 9 is in column 1, row 2: source rectangle (16, 32, 16, 16). Drawing is then "copy this source rectangle to this destination position".

Animation is a function of time

An animation is a list of frame numbers, a speed in frames per second, and a mode. The tempting implementation is to move to the next frame on every update. It is wrong, because then the animation runs faster on a 144 Hz machine than on a 30 Hz one. Instead each sprite keeps an elapsed time in whole milliseconds and the frame is a pure function of it:

step = elapsed * fps / 1000        (integer division)
  • loop: show frame list entry step mod length.
  • once: show entry step, but never past the last one; the animation holds its final frame, which is how an explosion or a death animation stays finished.

Whole milliseconds and integer division keep this exact; accumulating fractional seconds drifts and rounds differently between machines. Switching animation (say from walking to jumping) restarts the clock at 0, otherwise the jump would begin halfway through.

Take 10 fps, looping over frames 8, 9, 10:

elapsed   99   100   250   300   650
step       0     1     2     3     6
shows      8     9    10     8     8

At 99 ms there has not yet been one full step (990 / 1000 rounds down to 0); at 100 ms the animation moves on.

Draw order

The painter's algorithm draws far things first so near things cover them. Sprites are grouped into layers (background, world, effects, UI): lower layers first. Inside a layer, a top-down game sorts by y so a character standing lower on the screen draws over the one behind it. Finally the id breaks ties, so two sprites at the same depth always draw in the same order and the picture cannot flicker between frames. Sorting a copy of the list leaves the original order untouched.

Common mistakes

  • Advancing the frame once per update instead of deriving it from elapsed time.
  • Using mod for a once-only animation, which makes the explosion play over and over.
  • Swapping rows and columns when turning a frame number into a rectangle.
  • Not resetting the clock when switching animations.
  • Leaving ties unordered. Two sprites with the same layer and y must still have one fixed order.

Your exercise

Implement the three small functions described in the exercise: the current frame, its source rectangle, and the draw order. The starter parses the script, advances the clocks and prints each frame's draw list. The most likely mistake is using the wrong rule at the end of a once-only animation.

Up nextAudioResources & Rendering

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…