Pyrge Tutorial 4: Mixins

in Programming

It’s been about a month since the last tutorial, and two versions of Pyrge have come and gone since then. Hopefully you didn’t mind the wait, and you won’t mind waiting a bit longer for a more interesting tutorial. This time, we’re just bridging the gap, so to speak.

Round and Round

When we last left our intrepid hero, he had just learned how to walk around and shoot his gun. There’s one problem with his walking about, though. What happens when he hits the side of the “world”? Well, nothing really. He just keeps on going, off into the unknown (unknowable?) void.

Now, that’s not very heroic of him, so let’s fix it. There are two ways we can go about it, though. First, we can just make the edge of the world solid and impassable. That’s easily accomplished by adding conditions to lines 30, 33, 36, and 39 of our last tutorial script. But there’s another, more interesting, option: wrapping! Take a look at “tutorial8.py”, shown here:

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
68
69
70
71
from pyrge import *
 
class TutorialWorld(World):    
    def __init__(self):        
        super(TutorialWorld, self).__init__()
        sprite = TutorialImage()
        self.add(sprite)
        self.followBounds()
 
class TutorialImage(Image, mixin.Wrapper):
    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, mixin.Wrapper):
    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)
 
        self.lifetime = 1200
 
    def update(self):
        super(TutorialBullet, self).update()
        self.lifetime -= Game.elapsed
        if self.lifetime < = 0:
            self.kill()
 
theGame = TutorialWorld()
theGame.loop()

Very little of the code has changed, but what a difference it makes! Now, the player character can move around without worrying about going off the screen. When he reaches the edge, he warps to the opposite side, just like in some of those old arcade games.

So, what’s changed? First, on line 8, you’ll notice that the TutorialWorld.update method has effectively been replaced by a call to followBounds(). This method comes from the World class, and it just defines the “range” of the world based on the numbers you pass to it. If you pass it nothing at all, like we did here, then it locks the game world to the size of the screen.

The player class is still the same, but with one small addition on line 10. Instead of inheriting only from Pyrge’s Image class, it also inherits from something called mixin.Wrapper. I’ll explain what Pyrge means by “mixin” later, for now just know that a class with this will wrap around when it reaches the edge of the game world.

Line 47 is where the bullet class starts, and that class is the big winner this time, in terms of new code. First, it also uses mixin.Wrapper as a base class, meaning that bullets will wrap around the screen just like the player does.

You might remember how we determined when to destroy a bullet object in the last tutorial. There, we decided that a bullet that was more than 10 pixels off the screen would be considered “dead”. But a bullet that wraps is never off the screen, so we need a new tactic. Lines 62-68 show one possibility. We give each bullet a “lifetime” property (set to 1200 milliseconds, or 1.2 seconds), and then, when the bullet updates, we’ll subtract the amount of game time from the bullet’s life. Once the lifetime hits zero, it’s time to die. Of course, you can play around with the lifetime property. Combining that with the velocity of the bullet gives you a quick and dirty way of making different kinds of weapons.

Mixin’ It Up

The key to wrapping the player and bullet sprites in the above tutorial was the Wrappable mixin. Mixins are Pyrge’s implementation of a programming concept found in some form in many languages, and known by many names: multiple inheritance, interfaces, traits, etc. But you don’t need to understand all that to use them. All you really need to know is that a Pyrge mixin is a little “bonus” class that adds one or two features to a sprite. In this case, the feature is screen-wrapping, but there are also mixins that make sprites bounce (Bouncer), fade out (Fader), and react to mouse clicks (Clickable). In each case, the mixin adds a bit of code to the sprite’s constructor or update method. It all happens behind the scenes, and the only thing you have to do is add the mixin to the list of a sprite’s base classes.

You can make your own mixins, too. They should inherit from the mixin.SpriteMixin class, as a convention. That class doesn’t do anything by itself; it’s up to you to do the work. Also as a convention, you can safely assume that your mixin will have access to any of the Image properties and methods, but you should at least document if you’re using those of Image subclasses. (The Bouncer mixin, for example, uses velocity, a property that isn’t present in Image, but is in Entity.)

Finally, mixins use Python’s multiple inheritance scheme, which means that the order of base classes can change the effects. Typically, when defining a class, it’s best to put mixins first if they add update code that will change the way a sprite is rendered. (The Wrapper that we used here only affects the sprite’s position, so it doesn’t really matter what the order is.)

Next Time

In the next tutorial, it’s time to get serious, because we’re adding an enemy. Well, at first he’s really more of a target, but, hey, we’ve got to start somewhere.

Pyrge 0.6 Released

in Programming

Version 0.6 of Pyrge, the Python Retro Game Engine, is now available! This is mainly a bugfix and cleanup release, with the only additions being for completeness rather than features. The highlights include:

  • The Image.redraw() method has been added, replacing the need for explicitly setting the “dirty” flag. This also means that sprites using “self.dirty = 2” to force redrawing every frame will now work properly.
  • The Clickable mixin now works when using a scaled game world.
  • The flatten() function (in util) now uses recursion to support more highly-nested sequences.
  • text.Text now accepts a keyword argument “static” to switch between “position as top-left” (“static=True”, default for Texts) and “position as center” (“static=False”, default for most other Images).
  • New GameLoop methods showCursor() and hideCursor().
  • New GameLoop properties (updated per-frame): mousepos and mousebuttons.

As always, Pyrge can be downloaded from Github.

Pyrge 0.5 Released

in Programming

Today I am proud to announce the release of version 0.5 of Pyrge, the Python Retro Game Engine. It’s been two weeks since the previous release, but even in that short amount of time there are a few new features:

  • The GameLoop and World classes now have a title property, which will be used as the game window’s titlebar caption. This can be changed at any time, but, for obvious reasons, will have no effect in a fullscreen game.
  • The TiledImage constructor now accepts either an Image object or a filename as a reference for its pattern. The old Pygame Surface option is still there, too.
  • Image has a few new bits: the filename argument for the constructor (meaning that you can now write Image('somefile.png') instead of Image().load('somefile.png')), the addFrame and removeFrame methods for finer animation control, a recursive kill method that now works on child sprites, and the redraw method for, well, redrawing.
  • Slow motion is now easier than ever, with the timeScale property of game worlds. (Set it to less than 1 for slowdown, more than 1 for speedup.)
  • Finally, the mixin module has a new member: YOrdered. This mixin changes a sprite’s depth in the drawing order based on its Y coordinate, giving that retro fake perspective effect. (In other words, sprites near the bottom of the screen will be drawn on top of higher-up sprites.)

Pyrge 0.5 can be downloaded from Github. This is a source-only release, as I’m still looking for help making a Windows installer. The installation instructions are the same as before, and can be found in an earlier post.

As usual, there are still a few bugs to be worked out, but that’s just how it is with anything new. Let me know if you find any!

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!

Pyrge 0.4 Released

in Programming

Valentine’s Day is usually about candy and flowers and chocolates and cards, but I don’t really have anyone to share that with. That might be bad for me, but that means that you get a brand new 0.4 version of Pyrge!

I know it’s only been a week since version 0.3 came out, but development is fast and furious as I find new things to add, and old bugs to fix! This is an incremental release, though, so there aren’t too many new things.

The one big addition is the ui module, giving you two types of buttons (regular and toggle) that are completely integrated with the game engine. You can change their colors, and use graphics or text, and, if that’s not enough, you can always make subclasses.

The ui module also comes with a third class: Console. This is what its name implies: a debug console that you can write to. This is for anyone that wants debug output, but doesn’t want to have (or need) a shell or terminal open while their games are running. The console doesn’t have too many options right now, but it’s a start for those who need it, and, as always, it works right along with the rest of the engine.

The rest of the brief change list includes:

  • A Text object whose autowidth property is set to True will now grow only to the right, instead of both left and right. This means that the left edge of the text will stay where you put it.
  • Text objects also now have support for newlines, which should be cross-platform, but that part is completely untested.
  • Images have a new redraw method, which forces a redraw in the next frame. (This replaces the old way of writing self.dirty = 1.)
  • Animations are better supported by both the Image.showFrame method, which goes to a specific frame number without animating, and the optional multiplier parameter of Image.addAnimation, that causes the animation frame changes to be slowed down by that factor. (For example, a multiplier of 3 will cause each frame to be displayed 3 times in succession before the next frame. In code terms, this means that [1,2,3] with a multiplier of 3 is a shorter way of writing [1,1,1,2,2,2,3,3,3].)
  • The new flatten function takes a sequence and returns a generator object that yields a flattened list, meaning that members of sublists (or tuples or any other sequence) become top-level elements. Example: The list [1,2,(3,4),[5,6,7],8], when flattened, produces [1,2,3,4,5,6,7,8].

Finally, tutorial files 6, 7, and 8 have been added to the distribution. I’m already working on a post that uses them. Until then, have fun coding!