We
built a bouncing ball (dot) in the previous couple projects -- now let’s move on to building
something a bit more dynamic. Instead of
bouncing a single point, we'll bounce a sprite (a digital design), and we'll animate the sprite (have it change) as
we move it.
To
make things a little simpler for this project, we'll assume the sprite is a fixed size: 3 pixels
by 3 pixels (3x3), tough with minor changes to the code, you can change the
size to anything that suits your fancy.
Step 1: Create a sprite
The
LED Matrix API has a simple way to create sprites. You create a text file
that has the sprite drawn in text.
Let’s
start with just a single 3x3 sprite, we'll animate it later. To create
the sprite, open a new file called ball0.txt, and input the following (or your
own design)
3a7
8fa
183
That's
a 3x3 sprite that is darkest in the center. It’s also not symmetrical –
it’s darker in the upper right corners, which will give it a cool affect later
when we animate it.
Of
course, if you don't like the above sprite, then change it. That's the power of programming it yourself.
A quick test program to see if our sprite looks like we want:
INSERT CODE
from rstem import led
led.erase()
led.sprite(LEDSprite("ball0.txt"))
led.show()
Step 2: Change point to
sprite
Now,
given the work you did in Bouncing Ball 2 Project, you're ready to change the
ball from a point to your sprite.
First,
we’ll initialize the sprite (create it) before jumping into our game loop. Next, we’ll change the line of code that
previously drew a single point to a line of code that will instead draw your
sprite. You'll need to set the position of the sprite when you draw it.
sprite
= LEDSprite("ball0.txt")
while
True:
...
led.sprite(sprite,
(int(x), int(y)))
...
If
you ran the program as-is, you’d find that the sprite will now go over the
right and top edge of the LED matrix – this is because our program is expecting
an bouncing object that is only 1 pixel in width, and it doesn’t recognize that
the edge is further out. Remember, the sprite is 3x3, and we are keeping
track of the lower left corner of the sprite in our x and y variables. We
need to adjust the collision with the left and top walls.
For
example, in the x direction, we need to adjust the collision with the wall by
the width of the sprite minus one:
if
x >= (led.width() - (sprite.width() - 1)) or x < 0:
and,
you'll need to do the same thing for the y direction with the height of the
sprite.
Step 3: Animate the sprite
Now,
to animate the sprite, we'll create four versions of the sprite, each rotated
90 degrees from the first one. For example, given the ball0.txt we
created before, here's ball1.txt:
183
8fa
3a7
You
can then create ball2.txt and ball3.txt using the same methodology.
Once
we have all four sprites created (ball0, ball1, ball2, and ball3), we are ready
to animate. We make a list of the
sprites, and rotate through them. You could
simply create the list using the following code:
sprites = [
LEDSprite("ball0.txt"),
LEDSprite("ball1.txt"),
LEDSprite("ball2.txt"),
LEDSprite("ball3.txt") ]
and
that will certainly work. But we're going to use a cool feature of Python
called list comprehensions. If you haven't used them before, they are a
simply way to create a list dynamically within the code, in an easy to read
format.
For
example, here's how we could create the list above using list comprehensions:
sprites
= [LEDSprite("ball%d.txt" % i) for i in range(4)]
The
list comprehension above says to create a sprite for i in the range 0 to 3.
For each sprite created, it replaces the %d in the name with the value of
i. The advantage of the above code is it works for any number of balls,
just by changing the number in the range() function.
Finally,
it’s time to animate the sprite. That is, every fixed amount of time, we
want to change the sprite from one animation to the next one in the list.
For each step in the game loop, we will be moving the ball's position.
However, we don't want to animate the sprite that frequently.
Therefore we'll only change the sprite every few steps:
step += 1
if step % sprite_steps == 0:
n = (n + 1) % len(sprites)
The
above code uses the modulus operator "%". Modulus is like
division, except that it returns the remainder, not the dividend. It’s a
handy tool when creating loops.
In
the if statement, its used to run the if statement once every strite_steps.
That is, step is incremented each time the above code is run, but
"step % sprite_steps" is only equal to 0 once every sprite_steps times.
The
second time modulus is used, it causes n to be incremented by one, but once n equals
len(sprites), its set back to 0.
Step 4: Even More
Try changing
the sprite. It doesn't have to be a square sprite, it can be a rectangle,
too. However, if it’s a rectangle that rotates, the collisions with the
walls can get tricky. We'll do more with collisions in a later project.