Python PyGame 3.1: Walkthrough the WWII Bombing Pilot Game

PyGame in Python Part 3.1 Walkthrough the WWII Bombing Pilot Game

So, we’ve introduced you to the basics of Python and PyGame over the last several blogs. Now it’s time to get your hands dirty and do something FUN! The next several blogs will be devoted to breaking down the game from Chapter 7 of “Game Programming: The L Line, The Express Line to Learning”.

What you will do: You will put everything together: images (sprites), sounds, user input, classes and objects, moving objects, collisions, etc. My goal will be to break it out in easy chunks.

The story: The story in the book has *obviously* been modified to be politically correct and avoid the subject it rips off: bombing islands in the South Pacific in WWII. I have no editor (clearly), so, I’ve restored the original intent to bombing anti-aircraft facilities scattered over islands. When you fly over an island, you bomb the target successfully. The clouds represent the frak from anti-aircraft cannons. If you touch them, you’ve just had shrapnel rip through parts of your plane. When you’ve been frakked 5 times, your engines fail and you take a short trip into the sea. If you’ve played any games like Modern Warfare… this should be like a trip to the ice cream parlor.

Obviously a game of this size takes a more serious approach to the structure. Additionally there are parts I really don’t care to cover in this blog (designing graphics) so I’m just going to have you take the graphics off the companion site. You could take the whole program, but, there’s no sense of accomplishment there.

So, let’s start by making a new directory under Python27/games (or whatever Python you are running) called 1945. Then go to this link: http://bcs.wiley.com/he-bcs/Books?action=resource&bcsId=3648&itemId=0470068221&resourceId=10183 and drag all the images and sounds over into your folder so we don’t have to worry about them later. Okay, that saves us a lot of trouble and explanations a book cannot avoid.

Ground Rules
1. The bomber plane only moves LEFT and RIGHT, and is controlled through the MOUSE.
2. The background scrolls down forever (seemingly) simulating the plane flying forward.
3. Islands move down screen.
4. When the plane hits an island; we have successfully bombed the enemy and a sound will play. You get a combat bonus of $1,000.
5. Anti-Aircraft fire moves around randomly. If the plane touches it, that’s holes in the fuselage and a sound will play. You’re now 1 step closer to those great white sharks.

First, we’ll make all the sprites…
Create a file called bomber_plane.py

Create a file called bomber_plane.py
""" bomber_plane
    step 1 of 1945 knockoff
    build plane sprite, 
    control it with mouse
"""
    
import pygame
pygame.init()

class Plane(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("plane.gif")
        self.rect = self.image.get_rect()
        
    def update(self):
        mousex, mousey = pygame.mouse.get_pos()
        self.rect.center = (mousex, 430)
		
def main():
    screen = pygame.display.set_mode((640, 480))
    pygame.display.set_caption("Bomber Pilot! bomber_plane.py - creating the Plane sprite")

    background = pygame.Surface(screen.get_size())
    background.fill((0, 0, 255))
    screen.blit(background, (0, 0))
    plane = Plane()
    
    allSprites = pygame.sprite.Group(plane)
    clock = pygame.time.Clock()
    keepGoing = True
    while keepGoing:
        clock.tick(30)
        pygame.mouse.set_visible(False)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                keepGoing = False
                
        allSprites.clear(screen, background)
        allSprites.update()
        allSprites.draw(screen)
        
        pygame.display.flip()
    
    #return mouse cursor
    pygame.mouse.set_visible(True) 
if __name__ == "__main__":
    main()

Let’s review some things here. At the tope we’ve imported and initialized the PyGame libraries. Then we setup the Plane class with a constructor/initialized and an update function. This is where we capture and respond to the user’s mouse. You can see we have fixed the Y-coordinate to the bottom of the screen but the X-coordinate goes where the user moves it.

In the main we build&blit the background, create an instance of the Plane class, setup the main loop and use the sprite group to manage screen updates. I haven’t gone over the sprites and sprite groups yet, so, let me summarize he explanation from PyGame’s tutorial that’s very well written:
The Sprite class is a base class for game objects. It only has methods to help it work with Group classes. The sprite keeps track of which groups it belongs to.
The Group class is a container. You can pass list of sprites to the constructor (__init__ method) to create a Group instance that contains some initial sprites. The sprites() method returns an object that can be looped on to access every sprite the group contains. The Group also has an update() method, which will call an update() method on every sprite in the group.
http://www.pygame.org/docs/tut/SpriteIntro.html


Next up is the Island Sprite. Hopefully you grabbed the graphics for this as I’m not going to describe the big fun of playing with pixel graphics. If you’re a programmer, I’m sure you don’t enjoy that part. Remember to give your nearest graphic designer a hug for taking on all the pain for you ;-)

Here’s a list of the behaviours we want to accomplish with this sprite:
1. Y-coordinate updates at a constant rate (it moves down)
2. X-coordinate is fixed
3. When it hits the bottom, it is re-drawn at the top
4. When it hits a plane, it is re-drawn at the top

Here’s the code:

class Island(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("island.gif")
        self.rect = self.image.get_rect()
        self.reset()
        
        self.dy = 5
    
    def update(self):
        self.rect.centery += self.dy
        if self.rect.top > screen.get_height():
            self.reset()
            
    def reset(self):
        self.rect.top = 0
        self.rect.centerx = random.randrange(0, screen.get_width())

Pretty basic. Notice in the update(self) function that it checks for the bottom screen “collision”. Notice in the reset(self) function how it randomly decides where to draw it at the top. It would be some kind of geographical miracle if the islands in the archipelago all lined up perfectly. Now we’ll modify the main():

    
def main():
    screen = pygame.display.set_mode((640, 480))
    pygame.display.set_caption("Bomber Pilot! bomber_plane.py - creating the Plane sprite")

    background = pygame.Surface(screen.get_size())
    background.fill((0, 0, 255))
    screen.blit(background, (0, 0))
    plane = Plane()
    island = Island()
    
    allSprites = pygame.sprite.Group(island, plane)
    clock = pygame.time.Clock()
    keepGoing = True
    while keepGoing:
        clock.tick(30)
        pygame.mouse.set_visible(False)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                keepGoing = False
                
        allSprites.clear(screen, background)
        allSprites.update()
        allSprites.draw(screen)
        
        pygame.display.flip()
    
    #return mouse cursor
    pygame.mouse.set_visible(True) 
if __name__ == "__main__":
    main()

Now the sprite.Group thing is adding some value. We we’ll just continue to add sprites to this and let the Group functions do the heavy lifting of updating all it’s contained sprites.

We’ll end with the third sprite, the Cloud. Again, we need to figure out the rules of the cloud:
1. Y-coordinate will move at random rates (speed)
2. X-coordinate moves (side to side)

class Cloud(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("Cloud.gif")
        self.image = self.image.convert()
        self.rect = self.image.get_rect()
        self.reset()

    def update(self):
        self.rect.centerx += self.dx
        self.rect.centery += self.dy
        if self.rect.top > screen.get_height():
            self.reset()
    
    def reset(self):
        self.rect.bottom = 0
        self.rect.centerx = random.randrange(0, screen.get_width())
        self.dy = random.randrange(5, 10)
        self.dx = random.randrange(-2, 2)

Nothing super new going on here. You can see the only collision we’re coding for is the bottom of the screen when we reset it. We don’t care if they move off the side. You see us having a variable “speed” with the line: self.dy = random.randrange(5, 10). The next line picks a random side to side drift.

def main():
    screen = pygame.display.set_mode((640, 480))
    pygame.display.set_caption("Bomber Pilot! bomber_plane.py - creating the Plane sprite")

    background = pygame.Surface(screen.get_size())
    background.fill((0, 0, 255))
    screen.blit(background, (0, 0))
    plane = Plane()
    island = Island()
    cloud = Cloud()
    
    allSprites = pygame.sprite.Group(island, plane, cloud)
    clock = pygame.time.Clock()
    keepGoing = True
    while keepGoing:
        clock.tick(30)
        pygame.mouse.set_visible(False)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                keepGoing = False
                        
        allSprites.clear(screen, background)
        allSprites.update()
        allSprites.draw(screen)
        
        pygame.display.flip()
    
    #return mouse cursor
    pygame.mouse.set_visible(True) 
if __name__ == "__main__":
    main()

Again, we’ve added the cloud sprite to the group allSprites and we’re done. Pretty awesome. Okay, so if you run the program now, you’re going to see something that’s not Call of Duty, but, far and away cooler than even the Mandelbrot set (unless you’re a Math person and then Mandelbrot is still way, way cooler):

Next time we’ll move on building sprites and adding sounds to the collissions. Thanks for making it this far. I’m still trying to find the best way to display code in these posts. So far, I like screenshots best for visuals, but it leaves a lot to be desired since you’d have to copy it by hand. I’ll figure it out eventually.

-Mike

© Copyright Duke Hall - Designed by Pexeto