Pyrge Tutorial 3: Basic Animation and Entities

in Programming

With the new version of Pyrge comes a new tutorial. This time our topic is animation, the heart of any game’s graphics. Without animation, the only games we’d have would be text adventures like Zork and those weird Japanese date sims. But enough contemplating, let’s get on with the show!

Moving Pictures

The first thing we need is an animated image, something that we can make move. I’m terrible at any kind of graphical endeavor, so I’ll use somebody else’s work. We’ll use this particular sprite as our player; according to wherever I found it, it’s from Ari Feldman’s spritelib. I don’t have a link right now, but I’m sure you can find it. Anyway, here’s our image:

Simple animation strip

I cut and pasted a little to make it into a long, thin strip, but the original was in two rows of four images. You’ll see why I did that when we explore the code. Oh, that’s right, the code! Well, here it is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from pyrge import *
 
class TutorialWorld(World):
    def __init__(self):
        super(TutorialWorld, self).__init__()
        sprite = TutorialImage()
        self.add(sprite)
 
class TutorialImage(Image):
    def __init__(self):
        super(TutorialImage, self).__init__(100,100)
        self.loadAnimation('animatedsprite.bmp')
        self.addAnimation('walkleft', [0,4], 8)
        self.addAnimation('walkright', [1,5], 8)
        self.addAnimation('walkup', [2,6], 8)
        self.addAnimation('walkdown', [3,7], 8)
        self.showFrame(0)
 
    def update(self):
        anim = None
        if Game.keys[Constants.K_LEFT]:
            anim = 'walkleft'
            self.x -= 2
        if Game.keys[Constants.K_RIGHT]:
            anim = 'walkright'
            self.x += 2
        if Game.keys[Constants.K_UP]:
            anim = 'walkup'
            self.y -= 2
        if Game.keys[Constants.K_DOWN]:
            anim = 'walkdown'
            self.y += 2
 
        if anim is not None:
            self.play(anim)
        else:
            self.stop()
 
        super(TutorialImage, self).update()
 
theGame = TutorialWorld()
theGame.loop()

If you’re following along at home, this goes in a file called “tutorial6.py”. Run it, and you’ll get an actual character on the screen, instead of the Pygame logo we had last time. What’s more, he really walks! Or as much as he can with two measly frames of animation for each direction. Still, that can be enough for some games.

Going through the code, we see that most of it is the same. We took out the mouse click function and the line where it’s added to the world. But what we’ve added is the real story.

Our new code starts on line 12. Instead of creating an image by using the load method, we’re using the loadAnimation method. If you expected this to do something other than load an animation, then you’re probably in the wrong place. Or a PHP coder. Same difference, really.

The next four lines (13-16) are pretty much identical. Each is a call to our sprite’s addAnimation method, which takes two required arguments: the name of the animation (we’re using ‘walkleft’, etc.) and a list of frames. But wait, what are these frames?

Look at the image we loaded. Notice how it’s 8 little guys, each in a different pose, all in a line? If you have a graphic like this, where the whole thing can be divided into a bunch of smaller graphics, then Pyrge can divide it for you. When we called loadAnimation, that’s exactly what happened. Just from the layout of the file, Pyrge gave our Image a list of 8 frames, numbered 0 through 7. Each frame is one part of the larger image, one pose of the character.

So now that we know that we have frames, let’s see what we can do with them. That means it’s back to the code. For each animation that we add, we have to tell Pyrge which frames to use for it. For example, the “walking left” animation will use the first and fifth frames (or 0 and 4, because programmers start counting from 0), “walking right” uses the second and sixth, and so on. We make a list of what frames an animation is going to use, and that’s our second parameter: [0,4], [1,5], [2,6], and [3,7].

OK, so what’s the third argument? What does the 8 mean? There’s nothing special about it. The third argument to addAnimation is optional, but it lets us control the speed of the animation by making that many copies of each frame. It’s a little hard to explain, but that means that our 2-frame list becomes a 16-frame animation, showing the first frame 8 times followed by the second frame 8 times. Since our game is currently running at 60 FPS, we need this to slow the animation to a reasonable speed. (By the way, you can only use whole numbers as factors, so there’s no way to speed up an animation short of raising the game’s framerate.)

On line 17, we call the showFrame method to give our character a starting point. Without this, the animation wouldn’t “kick in” until we started it, meaning that our sprite would only display its “base” graphic (the pixels property).

The update method contains the rest of the changes in this part of the tutorial. We’ll make a state variable, anim, that we’ll set to the name of the animation that we want to show, based on which arrow key is pressed. In lines 34-37, we use the play method to, well, play the animation that we selected, or stop animation altogether if none of the arrow keys are down. In other words, once he stops moving, so do his legs. He’d look pretty silly running in place, wouldn’t he?

That’s all there is to basic animation in Pyrge. Just load up a strip of frames, piece them together into frame lists, and, each frame, pick which one to show. One caution, though. Calling play will cause the sprite to start using the animation whose name you pass to it. But if you tell it to play the animation that’s already running, it won’t start over, unless you specifically tell it to. The play method has a second, optional, parameter for the starting frame. If you use it, the animation will start on that frame (or simply jump to it if you “replay” the current animation).

Bang Bang

Moving right along, we’ll step back from animation for now (there’s not really that much left to cover) and start using the second of Pyrge’s 3 main types of sprites: the Entity.

An Entity is a sprite that has a few extras. These extras can be summed up in one word: motion. Entity sprites have special properties that control how fast they can move in the game world’s space: velocity, acceleration, and drag. They can also rotate, and they can have rotational (or angular) velocity and acceleration. The class also includes three “hook” methods that are called when the sprite is moved, to allow for finer collision response.

Now that we know what they are, let’s see one in action. Here’s “tutorial7.py”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from pyrge import *
 
class TutorialWorld(World):
    def __init__(self):
        super(TutorialWorld, self).__init__()
        sprite = TutorialImage()
        self.add(sprite)
 
    def update(self):
        super(TutorialWorld, self).update()
        for b in self.getEntities(TutorialBullet):
            if b.x < -10 or b.x > Game.width+10 or b.y < -10 or b.y > Game.height+10:
                b.kill()
 
class TutorialImage(Image):
    def __init__(self):
        super(TutorialImage, self).__init__(100,100)
        self.loadAnimation('animatedsprite.bmp')
        self.addAnimation('walkleft', [0,4], 8)
        self.addAnimation('walkright', [1,5], 8)
        self.addAnimation('walkup', [2,6], 8)
        self.addAnimation('walkdown', [3,7], 8)
        self.showFrame(0)
        Game.world.addHandler(Game.events.KEYDOWN, self.fire)
 
    def update(self):
        anim = None
        if Game.keys[Constants.K_LEFT]:
            anim = 'walkleft'
            self.x -= 2
        if Game.keys[Constants.K_RIGHT]:
            anim = 'walkright'
            self.x += 2
        if Game.keys[Constants.K_UP]:
            anim = 'walkup'
            self.y -= 2
        if Game.keys[Constants.K_DOWN]:
            anim = 'walkdown'
            self.y += 2
 
        if anim is not None:
            self.play(anim)
        else:
            self.stop()
 
        super(TutorialImage, self).update()
 
    def fire(self, event):
        if event.key == Constants.K_SPACE:
            Game.world.add(TutorialBullet(self))
 
class TutorialBullet(Entity):
    def __init__(self, parent):
        super(TutorialBullet, self).__init__(position=parent.position, size=(4,4))
        v = 180
        Game.Draw.circle(self.pixels, Game.color('yellow'), (2,2), 2)
        if parent.currentAnimation == 'walkleft' or parent.currentAnimation is None:
            self.velocity = Vector(-v,0)
        elif parent.currentAnimation == 'walkright':
            self.velocity = Vector(v,0)
        elif parent.currentAnimation == 'walkup':
            self.velocity = Vector(0,-v)
        elif parent.currentAnimation == 'walkdown':
            self.velocity = Vector(0,v)
 
theGame = TutorialWorld()
theGame.loop()

This example has a lot of new code, but it’s all pretty simple once you take it apart and look at it piece by piece. First off, lines 9-13 contain the game world’s update method. After handing off to Pyrge, what it does is use getEntities to go through every sprite in its display list, but only those that are from the TutorialBullet class. (We’ll see exactly what that is in a moment.) For each of these objects, we check to see if the position is off the screen by at least a few pixels. If it is, then we kill it. Sounds harsh, I know, but some things you just have to do.

The TutorialImage class only has two real changes. First, we add an event handler on line 24, calling the fire method whenever a button is pressed. A sprite object can do this itself by using the Game.world object, a link to whatever “world” is running the game.

The other big change is the fire method itself, on lines 48-50. It checks to see if the key that was pressed is the Space bar, and, if it is, adds a TutorialBullet to the game world.

The rest of the new code is in lines 52-64, the entirety of the TutorialBullet class. What we’re doing here is making the projectiles that our character will shoot. First off, the constructor has an extra parameter, parent. That’s why we used TutorialBullet(self) to create the sprite, and that lets us use the “parent” sprite’s position as the basis for the bullet, so it looks like it’s shooting out of him.

Line 55 sets a variable that we’ll use shortly. Line 56 draws a yellow circle on the bullet’s pixels, using Pygame’s circle-drawing function. After that, lines 57-64 figure out which direction the character is facing by which animation is playing, each direction giving the bullet a different velocity. (You can change how fast the bullet travels by changing the value on line 55.)

Notice the one thing that the TutorialBullet class doesn’t have: an update method! Well, it has one, but only the one it gets from being an Entity. We don’t have to write code to figure out how to make something move at the right speed. Just set the velocity, and off we go (or off it goes, whatever).

Play around with this one for a while; try changing the velocity, or maybe make a “machine gun”, or just change the position of the bullets so that they look like they’re coming out of the barrel of the character’s gun, instead of his chest! There’s so much you can do with Entities even without customizing their updating code.

Next time, we’ll start the long and difficult process of producing something resembling a game from all of this. We’ll get a background, for one thing. And we’ll stop our little guy from running off the screen. See you next time!