A PyGame Working Example: Starting a Game

In PyGame for Game Development, I showed you the very basics of PyGame’s graphical side. However, creating a game with PyGame requires a bit more. All the concepts described before need to be glued together somehow, and new concepts will need to be introduced in order to create a functional game. In this article, we’ll do just that by tackling a working example of PyGame’s capabilities—a Python-powered game.

The Game Plan

Before we jump into Python code, we need to develop the idea behind our game. Nothing complex is needed for our game, since its purpose is just to reinforce the basics of PyGame and demonstrate how to put them to use. With that said, let’s move into the idea. The idea is to have the player at the bottom of the screen with the ability to move horizontally. Horizontal movement will be required to dodge incoming objects that will constantly be hurled at the player at fast speeds. Picture the player as a ship having to navigate at high speeds (which will be essentially an illusion, since the player will not be moving vertically; the incoming objects will) and dodge mines or enemy ships.

However, to give it an added degree of complexity and to make it more dynamic, levels will be stored as Python objects. Each level object will be responsible for loading the required images and laying them out in a layout list. The layout list will contain many more lists as elements that correspond to rows of objects. So, for example, picture a layout list as looking something like this:

[[0, 0, 0, 0],

 [1, 0, 1, 0],

 [1, 0, 1, 0],

 [1, 0, 1, 0],

 [0, 0, 0, 0]]

A zero would be mapped to a blank space, and a one would be mapped to an object of some sort. The result would look something like this, to draw a very rough sketch with gridlines for visibility:


The green block represents the player, and the red blocks represent the objects that the player is required to dodge. We would simply have to move the objects down and see if the player has collided with any of the objects.

Of course, since the levels would be generated by Python classes that determine what the images are and how they are positioned, levels would not be limited to the mine-dodging scenario I presented earlier. You could have a fish dodging sharks or a spaceship dodging asteroids if you felt inclined to do so.

Also, it’s possible to create random object layouts or images using this method. This way, levels could be different each time they are played.

{mospagebreak title=Preparing a Level}

Let’s start by making a level and building the game around it. The idea is to create the level out of an object that loads the required images and returns the layout list described in the last section. Before we do that, though, let’s create a class from which all level classes can be derived. That way, we can organize things a bit better:

class Level:

   def getPlayer(self):
      pass
   def getObjects(self):
      pass
   def getLayout(self):
      pass
   def getBackground(self):
      pass

Save the script as levelbase.py.

With the actual levels that come from the Level class, our game will be concerned with four methods: getPlayer, getObjects, getLayout and getBackround. The first method will be responsible for returning the player’s image. The second method will be responsible for returning a list of the images of the objects involved. The third method will be responsible for returning the layout list. The final method returns the background image, but it will also need to return the number of rows visible on the screen at a time. This way, we can divide up the background and figure out how long each space will be. The width of each space will be determined by measuring how big the first element (which will be a list itself, of course) is inside of the layout list.

Each element of the layout list will be a number. Zero will represent a blank space, and one and above will represent objects. These objects will be based on images obtained from getObjects, but since the list index will start at zero, we’ll have to subtract from each of the elements in getLayout to obtain the proper image. So, to express this in more basic terms, a value of 1 in getLayout would correspond to the first object in getObjects (0), and a value of 2 in getLayout would correspond to the second object in getObjects (1).

{mospagebreak title=Creating a Level}

Now, we’re ready to create a level for our game. Name this asteroid.py:

import levelbase
import pygame

class Level(levelbase.Level):

   def getPlayer(self):
      return pygame.image.load(‘ship.gif’)
   def getObjects(self):
      return [pygame.image.load('asteroid.gif')]
   def getLayout(self):
      return [[1, 1, 1, 1, 0, 0, 1, 1, 1, 1],
              [0, 1, 1, 0, 0, 0, 0, 1, 1, 0],
              [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
              [1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
              [1, 1, 1, 0, 0, 0, 1, 1, 0, 0],
              [0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
              [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
              [1, 1, 0, 0, 0, 0, 1, 0, 0, 0],
              [1, 1, 1, 0, 0, 0, 0, 0, 0, 0]]
   def getBackground(self):
      return pygame.image.load(‘background.gif’), 5

As you can see, we simply return the required images, along with the layout list and the number of rows that will be visible at once. This particular level goes with one of the ideas I described above: a spaceship dodging asteroids. Here are the images that the level references:

ship.gif


asteroid.gif


background.gif


Notice how the background is not solid, but, rather, it’s an image. Because of this, we can’t simply erase a sprite by drawing a solid-colored Surface object over it. Instead, we have to get the original background image that the sprite replaced and then draw it over the sprite. Thankfully, this is very simple, and we’ll look at how it’s done later on.

{mospagebreak title=Sprite Definitions}

Next, we’ll need to define the classes for player sprites as well as object sprites. Since the player sprite has no ability to move vertically, it will only have to be moved by its x-position. So, the player class’s update method will only need to accept the amount its x-position needs to move. Likewise, because objects will have a static x-position, the object class’s update method will only need to accept the amount to update the y-position. Also, our game will move objects by their center positions, not by their upper-left corners. We’ll use centerx and centery rather than x and y.

import pygame

class Player(pygame.sprite.Sprite):

   def __init__(self, image, xCenter, yCenter):

      pygame.sprite.Sprite.__init__(self)

      self.screen = pygame.display.get_surface().get_rect()
      self.image = image
      self.rect = self.image.get_rect()
      self.rect.centerx = xCenter
      self.rect.centery = yCenter

   def update(self, xAmount):

      self.rect = self.rect.move([xAmount, 0])

      # Check to see if we have gone offscreen
      if (self.rect.x < 0) or (self.rect.x >= self.screen.width):
         self.rect = self.rect.move([-xAmount, 0])

class Object(pygame.sprite.Sprite):

   def __init__(self, image, xCenter):

      pygame.sprite.Sprite.__init__(self)

      self.image = image
      self.rect = self.image.get_rect()
      self.rect.centerx = xCenter

   def update(self, yAmount):

      self.rect = self.rect.move([0, yAmount])

Save the script as gamesprites.py. We’ll import it as a module when we need to create the sprites for our game. The Player class accepts three arguments when it is initialized: the player image, its center x-position and its center y-position. Note that the player image will already be loaded onto a Surface object by the level, so there’s no need to load it again. The Object class only accepts two arguments, the object’s image and the center x-position. Since all objects will start at the top of the screen, there’s no need to accept any y-position. The update methods simply accept the amount the sprites need to move, with each class moving in a different direction, as described before. Additionally, the Player class checks to see if the sprite has gone off the screen. If it has, we undo any movement to put it back on the screen.

The Internals

The functional code of the game will make up its own module, and related Python instructions will be grouped into the module’s methods. This is so we can customize the game later on by importing the module, calling whatever methods we need to call, and passing whatever level we want to play rather than modifying the internals of the game. Using this method of organizing the game, it’s possible to add extra features such as a menu rather than limiting it to one game. This also allows us to create custom “Game Over” or success screens rather than relying on a generic one or modifying the internals to create a more specific one. We could also create either a linear or semi-dynamic campaign by going this route.

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye