Exerpad
🔥 1
10 XP
Lesson · Chapter: Draw a Sprite+10 XP for reading

Shapes as Data

A single dot has carried you a long way. But a game character is a shape, and you don't want to write eight grid[...] = ... lines every time it moves.

Two ideas fix this, and they are the last two before you build a real game.

1. A shape is a list of lists

Draw the picture in your code and read it as data:

python
HERO = [
[0, 1, 1, 0],
[1, 1, 1, 1],
[0, 1, 0, 1],
]
Output
Press Run to see the output.

1 means "paint this square", 0 means "leave it". That's a three-row, four-column sprite you can actually see in the source — which makes it easy to change.

2. Write a function that stamps it

python
def stamp(grid, sprite, top, left, colour):
for r in range(len(sprite)):
for c in range(len(sprite[r])):
if sprite[r][c] == 1:
grid[top + r][left + c] = colour
Output
Press Run to see the output.

top and left say where the sprite's corner goes. Everything inside is worked out from there.

Now drawing the hero anywhere is one line:

python
stamp(grid, HERO, state["y"], state["x"], "gold")
Output
Press Run to see the output.

You have written functions before, in Milestone 1. This is the first time one has really earned its keep: the alternative is twelve lines every frame, and twelve chances to make an off-by-one mistake.

len(sprite) and len(sprite[r])

len(sprite) is the number of rows. len(sprite[r]) is the number of columns in row r.

Writing it that way means the same stamp works for a 3×4 hero, a 5×5 coin, or a 1×1 dot. Hard-code 3 and 4 and you've written a function that only draws one thing — which is barely a function at all.

Staying inside the picture

stamp will happily try to paint at row 20, and Python will stop you with an IndexError. Keep the sprite inside the grid, or check before painting:

python
if 0 <= top + r < HEIGHT and 0 <= left + c < WIDTH:
grid[top + r][left + c] = colour
Output
Press Run to see the output.

That guard is worth adding once you start moving sprites around — which is the very next chapter.