For this tutorial I’m going to take a look at PyGlet: “a cross-platform windowing and multimedia library for Python.” The reason that I decided to take a look at PyGlet is because it is an alternative to PyGame in the Python gaming world and: “No external dependencies or installation requirements. For most application and game requirements, pyglet needs nothing else besides Python, simplifying distribution and installation.”
The first step to using PyGlet is to actually download it and install it (http://pyglet.org/download.html) as of writing this PyGlet is at version 1.0 alpha 2 (as I finished this tutorial Beta 1 was released but I have been unable to try it out), so between now and the final release there are bound to be a few changes. Once you have download the correct version for your system install it an you are ready to go. I’m writing this on a Debian box so I downloaded the source distribution and installed it using the following, as per the instructions:
python setup.py install
Now like PyGame, PyGlet is a framework for developing games or other applications, it is not a game engine, therefore if you are looking to create a full game you will need to create your own, or use someone else’s. This tutorial will not going into full game creation, instead it will introduce PyGlet using a small sample application, hopefully giving you enough of the basics, or enough of a taste to continue on with it.
You can download the full source to this tutorial here.
Creating a Window
The first step is to get an actual window displaying. In this initial code I’m going to create a window with a main game loop and display the Frames Per Second that the game is currently running at. In order to do this I am going to subclass from the pyglet.window.Window class. You don’t have to do this in order to display a window, but given the way that events are handled this seemed to make the most sense to me.
I added the following to a file named “PyGletSpace.py”
from pyglet import window from pyglet import clock from pyglet import font class SpaceGameWindow(window.Window): def __init__(self, *args, **kwargs): #Let all of the standard stuff pass through window.Window.__init__(self, *args, **kwargs) def main_loop(self): #Create a font for our FPS clock ft = font.load('Arial', 28) #The pyglet.font.Text object to display the FPS fps_text = font.Text(ft, y=10) while not self.has_exit: self.dispatch_events() self.clear() #Tick the clock clock.tick() #Gets fps and draw it fps_text.text = ("fps: %d") % (clock.get_fps()) fps_text.draw() self.flip() if __name__ == "__main__": # Someone is launching this directly space = SpaceGameWindow() space.main_loop()
So we are not doing anything that fancy here, first we import three things from the PyGlet library, the window, clock, and font modules. We then create a class called SpaceGameWindow which is a subclass of the pyglet.window.Window class.
Then we have a simple initialization function where for now we simply pass all of the keywords and arguments to the pyglet.window.Window’s __init__() function.
Next we have our main_loop() function which will serve as the main loop for the game. Here we create a pyglet.font.base.Font object and a pyglet.font.Text object that will serve as the font for our “Frames Per Second” text, and the class that will actually write out the “Frames Per Second” text.
After that we have the actual game loop where we continue to loop until the window’s has_exit boolean member variable becomes true.
In the loop we first dispatch all of the event to the event handlers, and then we clear the window to prepare it for drawing. Then we tick the clock, get our FPS text and then draw it out to the screen. Then, since windows in PyGlet are double buffered we flip the display.
Hopefully that is pretty straight forward, if you run the code you should see something similar to the following:
Pretty good so far but this doesn’t really help us or do anything, so the next step is to get some stuff draw onto the screen. To do that I’m going to create a simple Sprite class. This class will be loosely based off of the PyGame’s Sprite class, but for the most part will serve as a helper for drawing and the action on the screen.
class Sprite(object): def __get_left(self): return self.x left = property(__get_left) def __get_right(self): return self.x + self.image.width right = property(__get_right) def __get_top(self): return self.y + self.image.height top = property(__get_top) def __get_bottom(self): return self.y bottom = property(__get_bottom) def __init__(self, image_file, image_data=None, **kwargs): #init standard variables self.image_file = image_file if (image_data is None): self.image = helper.load_image(image_file) else: self.image = image_data self.x = 0 self.y = 0 self.dead = False #Update the dict if they sent in any keywords self.__dict__.update(kwargs) def draw(self): self.image.blit(self.x, self.y) def update(self): pass def intersect(self, sprite): """Do the two sprites intersect? @param sprite - Sprite - The Sprite to test """ return not ((self.left > sprite.right) or (self.right < sprite.left) or (self.top < sprite.bottom) or (self.bottom > sprite.top)) def collide(self, sprite_list): """Determing ther are collisions with this sprite and the list of sprites @param sprite_list - A list of sprites @returns list - List of collisions""" lst_return =  for sprite in sprite_list: if (self.intersect(sprite)): lst_return.append(sprite) return lst_return def collide_once(self, sprite_list): """Determine if there is at least one collision between this sprite and the list @param sprite_list - A list of sprites @returns - None - No Collision, or the first sprite to collide """ for sprite in sprite_list: if (self.intersect(sprite)): return sprite return None
So it may look complicated but it’s really not that difficult. First off you will see that I create some positioning properties, left, right, top, and bottom. I do this so that the collision code is easier to read, plus it separates the positioning information from the implementation, in this case the pyglet.image.AbstractImage member. If, in the future we were to add a rectangle class to the Sprite we don’t have to change a the code that interacts with the positioning, just what the position properties interact with.
You will also notice that in the Sprite class can either be initialized using an image file or the actual image data, which is a pyglet.image.AbstractImage. The reason for this is instead of having to load the same image from the hard drive time and time again, and to avoid having the same image duplicated in memory in countless locations, we let the user decide which makes more sense.
Other then that it’s pretty simple stuff, some positioning information, and whether or not the sprite is Alive. If a sprite is dead it will be removed from the window. There is also a draw method where the sprites image is blited to the window, and an update function where (if necessary) the sprite should update itself (position, image, etc).
Next we have a few collision functions that will help us to determine when out sprites collide. Now that we have the sprite class we need to add three more classes based off of the Sprite class. A SpaceShip class, a Bullet class which will be fired by the SpaceShip, and a Monster class.
class SpaceShip(Sprite): def __init__(self, text_x, text_y, **kwargs): self.kills = 0 Sprite.__init__(self, "ship.png", **kwargs) #Create a font for kill message self.font = font.load('Arial', 28) #The pyglet.font.Text object to display the FPS self.kill_text = font.Text(self.font, y=text_y, x=text_x) def draw(self): Sprite.draw(self) self.kill_text.text = ("Kills: %d") % (self.kills) self.kill_text.draw() def on_kill(self): self.kills += 1
The SpaceShip class doesn’t do that much above and beyond the base class. It has a data member “kills” that keeps track of the number of monsters that it kills, and a pyglet.font.Text member that will display the number of kills on the screen. The position of the “kill text” is also passed into the SpaceShip constructor.
class Bullet(Sprite): def __init__(self, parent_ship, image_data, top, **kwargs): self.velocity = 5 self.screen_top = top self.parent_ship = parent_ship Sprite.__init__(self,"", image_data, **kwargs) def update(self): self.y += self.velocity #Have we gone off the screen? if (self.bottom > self.screen_top): self.dead = True def on_kill(self): """We have hit a monster let the parent know""" self.parent_ship.on_kill()
The Bullet class will represent bullets fired by the SpaceShip class, so it adds three new data members the:
1) The velocity – which controls how fast the bullet moves.
2) The screen_top – Which represents the top of the screen is and will be used to destroy the bullet when the bullet moves past the top of the screen.
3) The parent_ship – This is the SpaceShip that fired the bullet. This will be used to let the ship know when it has made a kill.
The final sprite is the Monster sprite:
class Monster(Sprite): def __init__(self, image_data, **kwargs): self.y_velocity = 5 self.set_x_velocity() self.x_move_count = 0 self.x_velocity Sprite.__init__(self, "", image_data, **kwargs) def update(self): self.y -= self.y_velocity self.x += self.x_velocity#random.randint(-3,3) self.x_move_count += 1 #Have we gone beneath the botton of the screen? if (self.y < 0): self.dead = True if (self.x_move_count >=30): self.x_move_count = 0 self.set_x_velocity() def set_x_velocity(self): self.x_velocity = random.randint(-3,3)
The Monster sprite will be moving from the top of the screen to the bottom of the screen, which is the opposite of the Bullet and is controlled by the y_velocity data member. But unlike the bullet the Monster sprite will also have some horizontal motion built it, which is controlled by the x_velocity data member, and set in the set_x_velocity() function. What is interesting about the x_velocity is that there is a bit of randomness built into it, it will be a random number from -3 in the left direct to 3 in the right direction. It will also change every thirty updates, giving the Monster a lazy side to side motion making it a bit more difficult for the SpaceShip to shoot it.
The Monster sprite does not have a bottom data member to tell it when it has moved off of the screen. The reason for this is that since the Monster will be moving down from the top of the screen, and we will can tell if it’s off the screen when it’s y, or top, data members are less then zero.
Adding the Sprites to the Game
Now that we have our sprite classes we need to use them in the main SpaceGameWindow class. There are a few ways to do this, but what I ended up doing was creating three new data members: ‘bullets’ and ‘monsters’, both lists of sprites, and ‘ship’ which will be the one and only ShapShip sprite.
The Sprites and some other data is initialized in the init_sprites() function:
def init_sprites(self): self.bullets =  self.monsters =  self.ship = SpaceShip(self.width - 150, 10, x=100,y=100) self.bullet_image = helper.load_image("bullet.png") self.monster_image = helper.load_image("monster.png")
All we do is create the two lists to store our sprites, the SpaceShip sprites, and use our helper to load the images that we will use for the Bullet and Monster sprites.
The next step is to change the draw loop so that we actually draw our sprites. There are a few more changes to be made in the main_loop() function but overall it remains pretty simple:
def main_loop(self): #Create a font for our FPS clock ft = font.load('Arial', 28) #The pyglet.font.Text object to display the FPS fps_text = font.Text(ft, y=10) #Schedule the Monster creation clock.schedule_interval(self.create_monster, 0.3) clock.set_fps_limit(30) while not self.has_exit: self.dispatch_events() self.clear() self.update() self.draw() #Tick the clock clock.tick() #Gets fps and draw it fps_text.text = ("fps: %d") % (clock.get_fps()) fps_text.draw() self.flip()
There is quite a bit happening here so I’ll take a little bit of time to explain everything that is new. One of the things that I want to happen is to have monsters created after a certain amount of time has passed. To to this I use the clock.schedule_interval function.
#Schedule the Monster creation clock.schedule_interval(self.create_monster, 0.3)
What this will do is call the create_monster() function every 0.3 seconds. The create_monster() function is pretty simple, it simply creates a Monster sprite in a random location provided that there is less then the maximum amount of Monsters currently around:
def create_monster(self, interval):
if (len(self.monsters) < self.max_monsters):
, x=random.randint(0, self.width) , y=self.height))
max_monsters is defined in the __int__() function to be 30. The next new feature is the limiting of the frame rate. For such a simple game I limit it to 30 frames per second using the set_fps_limit function.
Other then that there are only two new calls in the main main loop and that is:
update() is basically a function that is used to update everything that is happening on the screen: sprite positions, scores whatever you want. Then the draw() function is basically the function where everything that needs to be drawn onto the screen gets drawn.
def update(self): to_remove =  for sprite in self.monsters: sprite.update() #Is it dead? if (sprite.dead): to_remove.append(sprite) #Remove dead sprites for sprite in to_remove: self.monsters.remove(sprite) #Bullet update and collision to_remove =  for sprite in self.bullets: sprite.update() if (not sprite.dead): monster_hit = sprite.collide_once(self.monsters) if (monster_hit is not None): sprite.on_kill() self.monsters.remove(monster_hit) to_remove.append(sprite) else: to_remove.append(sprite) #Remove bullets that hit monsters for sprite in to_remove: self.bullets.remove(sprite) self.ship.update() #Is it dead? monster_hit = self.ship.collide_once(self.monsters) if (monster_hit is not None): self.ship.dead = True self.has_exit = True
I won’t explain this function too much since in general it’s pretty straight forward. We are basically updating all of the sprites, checking for collisions or them being “dead” and if the sprites need to be removed then we do.
The first step is to update all of the monsters. The second step is to update all of the Bullet sprites and if they are not dead (off the screen) check for a collision between them at the Monster sprites. If the Bullet hits a Monster sprite then it and the monster are both dead and will be removed.
Then we check to see if the SpaceShip sprite has collided with any of the Monster sprites. If it has we signal the end of the game by setting the has_exit window handler.
The draw function is very simple, we simply draw all of the sprites:
def draw(self): for sprite in self.bullets: sprite.draw() for sprite in self.monsters: sprite.draw() self.ship.draw()
The next step to setup some event handling, which was the whole reason that we made our main class use pyglet.window.Window as the base class way back before we got sidetracked on these sprites and game play.
Fortunately for us adding event handlers is very simple, we simply have to define one of the event functions
Since we want to use mouse motion to move the space ship we need to catch the mouse motion event.
def on_mouse_motion(self, x, y, dx, dy): self.ship.x = x self.ship.y = y
Pretty simple! We just need to add the function for the even and as long as we call dispatch_events our event will be handed.
We also want the spaceship to fire when we click the left mouse button. So to do that we need to handle the mouse press event.
def on_mouse_press(self, x, y, button, modifiers): if (button == 1): self.bullets.append(Bullet(self.ship , self.bullet_image , self.height , x=x + (self.ship.image.width / 2) - (self.bullet_image.width / 2) , y=y))
Here we simply make sure that it was the left mouse button that was pressed, and it it was we create a bullet in the correct location using the correct image.
PyGlet does something interesting it gives you a mouse drag event, which is sent when a mouse button is down and the mouse is moved. During the “mouse drag” event the “mouse motion” event will not be sent. Sine the used will be clicking a lot we need to keep the Space Ship moving by handling the mouse drag event, if we don’t the ship will seem to pause if we move the mouse and click the left mouse button.
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): self.ship.x = x self.ship.y = y
This is exactly the same as the mouse motion handler, and in truth it could call on_mouse_motion() itself.
You might have noticed that I use a helper to load this images. This is pretty simple stuff located in a file called “helper.py” in the same directory as PyGletSpace.py:
import os from pyglet import image def get_image_dir(): """Get the directory used to store the images @returns string - the directory """ directory = os.path.abspath(os.path.dirname(__file__)) directory = os.path.join(directory, 'data') return directory def load_image(image_file_name): full_path = os.path.join(get_image_dir(), image_file_name) return image.load(full_path)
You can download the full source to this tutorial here.
That’s it for this wonderfully short game and lengthy tutorial. What’s I’ve found is that PyGlet is an extremely interesting library that I will continue to keep an eye on. At a certain point I had heard that it was going to be including some Sprite classes and collision classes as well, but it seems as though this may be on the back burner for the initial release.
It will be interesting to see where this goes, what I would like to see would be a simple 2d (since that’s all I’m interested in fooling around with for the most part) engine built on top of PyGlet. A simple way to work with all of the different features that a 2d game needs. Hopefully something like that will come along at some point, heck maybe it’s already out there and I just don’t know about it yet. But if anyone is working on one, or interested in setting something like this up drop me a line.