Balloon Ninja: Releasing balloons

Back to Tutorial Contents

Showing a balloon

In the previous section we created an empty game window, and gave the window a background color. Now let’s add a balloon to the screen.

First we need to describe what a balloon is.  This is a class file, so save the following code as “Balloon.py”, with a capital B. Save it in your “balloon_ninja” directory.

import pygame
from pygame.sprite import Sprite

class Balloon(Sprite):

    def __init__(self, screen):

        Sprite.__init__(self)
        self.screen = screen
        self.image = pygame.image.load('green_balloon_50px.png').convert_alpha()
        self.image_w, self.image_h = self.image.get_size()

        self.x_position = 0
        self.y_position = 0

    def blitme(self):
        draw_pos = self.image.get_rect().move(self.x_position, self.y_position)
        self.screen.blit(self.image, draw_pos)

We have just created a class called “Balloon”. This file does not do anything by itself; it just defines what a balloon is, and what a balloon can do. Let’s look at what this says about a balloon. Every class needs an __init__ function, which will be called when we ask Python to create a balloon. For now our balloon only needs one piece of information – it needs to know what screen it will draw itself on. The word “self” refers to the current balloon that is being dealt with. If you are new to classes, don’t worry about this much for now. Basically, it will allow us to create a bunch of separate balloons using the same code, and each balloon will be able to refer to “itself”.

Our balloon is really just an image. We need to tell pygame where to find our balloon image. I started with a larger balloon image and resized it, but you can right click and save a small version of the balloon I used. You can use any image you want; if you wanted to change this game to “guido_ninja.py”, you could use this image. Just make sure the name of the file you save matches the name of the file in your Balloon.py code, on line 11.

The balloon we are starting out with.

The balloon we are starting out with.

In line 12 we store the width and height of our balloon image. We will use these values to draw the balloon centered on the point we are interested in. Without these values, the balloon image would be drawn with one of its corners at whatever point we specify. In lines 14 and 15, we make the balloon start out at the screen coordinates 0,0. This is the upper left position, as we will see in a moment. If you are new to screen coordinates, it is useful to note that the y-axis has its zero at the top of the screen, where we tend to start reading. This is also a useful convention in that we always have a top of a window, but many windows, such as browsers, can extend off the screen at the bottom.

The blitme function is critical to understanding how to use Pygame. The word “blit” refers to redrawing an object to the screen. Good game animation is all about managing how, when, and where you draw objects that have changed. Pygame manages most of this for us, and the blitme function is where we specify what needs to be redrawn. In this case, we tell the balloon to draw itself at its current x and y position. The screen.blit applies this drawing to our screen.

The beauty of classes is that we can now create as many balloons as we want! We will start by creating just one, but we will soon be creating a whole bunch of balloons, from this small code block. Let’s add some code to our balloon_ninja.py file, to make a balloon appear on our screen:

import pygame
import sys
from Balloon import Balloon

def run_game():
    # screen and game parameters
    screen_width, screen_height = 800, 600
    bg_color = 150, 150, 150

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (screen_width, screen_height), 0, 32)

    # Create our first balloon
    new_balloon = Balloon(screen)

    # main event loop
    while True:

        # Check for game events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        # redraw the screen, every pass through the event loop
        screen.fill(bg_color)

        # Draw our balloon on the screen
        new_balloon.blitme()

        pygame.display.flip()

run_game()

You should see a balloon appear in the top left corner of the screen:

A balloon appears in the top left of the screen, at screen coordinates 0,0.

A balloon appears in the top left of the screen, at screen coordinates 0,0.

Positioning a balloon

We want to make a few changes to our Balloon.py code.  First of all, we want our balloons to appear at the bottom of the screen and then rise up.  The bottom of the screen has a y-value equal to the height of our screen.  Let’s change the initial y-value of our balloon to the bottom of the screen:

import pygame
from pygame.sprite import Sprite

class Balloon(Sprite):

    def __init__(self, screen):

        Sprite.__init__(self)
        self.screen = screen
        self.image = pygame.image.load('green_balloon_50px.png').convert_alpha()
        self.image_w, self.image_h = self.image.get_size()

        self.x_position = 0
        self.y_position = self.screen.get_height()

    def blitme(self):
        draw_pos = self.image.get_rect().move(self.x_position, self.y_position)
        self.screen.blit(self.image, draw_pos)

The screen object has a method that can give us the current height of the screen, so in line 14 we use this method to place our balloon at the bottom of the screen. But when we run balloon_ninja.py, we don’t see a balloon anymore. This is because the balloon is drawn with its top left corner at the position we specify. Let’s make a small adjustment to our balloon’s drawing function, so that the balloon is centered at its current position:

import pygame
from pygame.sprite import Sprite

class Balloon(Sprite):

    def __init__(self, screen):

        Sprite.__init__(self)
        self.screen = screen
        self.image = pygame.image.load('green_balloon_50px.png').convert_alpha()
        self.image_w, self.image_h = self.image.get_size()

        self.x_position = 0
        self.y_position = self.screen.get_height()

    def blitme(self):
        draw_pos = self.image.get_rect().move(self.x_position-self.image_w/2, self.y_position-self.image_h/2)
        self.screen.blit(self.image, draw_pos)

Now in line 17 we draw the balloon at its current position, but it is shifted left by half of its width and up by half of its height. This centers the balloon at its current x and y coordinates:

Now the balloon appears centered at the bottom left of the screen.

Now the balloon appears centered at the bottom left of the screen.

This is better; the balloon is almost appearing where we want it to. So, where do we really want our balloons to appear? We want them to appear at a random x-position, and then rise up. The leftmost position a balloon can appear at should make the balloon’s left edge match up with the left edge of the screen, and the rightmost possible position should have the balloon’s right edge flush with the right edge of the screen. Here is the code to make our balloon appear at a random x-position:

import pygame
from pygame.sprite import Sprite
from random import randint

class Balloon(Sprite):

    def __init__(self, screen):

        Sprite.__init__(self)
        self.screen = screen
        self.image = pygame.image.load('green_balloon_50px.png').convert_alpha()
        self.image_w, self.image_h = self.image.get_size()

        self.x_position = randint(self.image_w/2, self.screen.get_width()-self.image_w/2)
        self.y_position = self.screen.get_height()

    def blitme(self):
        draw_pos = self.image.get_rect().move(self.x_position-self.image_w/2, self.y_position-self.image_h/2)
        self.screen.blit(self.image, draw_pos)

In line 14 we get a random x position for the balloon. We want the smallest x-value to be half of the balloon’s width. The greatest value should be the width of the screen, minus half of the balloon’s width. Now if we run balloon_ninja.py we should see something like this:

Now the first balloon appears in a random position at the bottom of the screen.

Now the first balloon appears in a random position at the bottom of the screen.

Making the balloon rise

Let’s make our balloon rise. To do this, we need to add some code to our main game file, and to the Balloon file. The Balloon file needs an update function, which will decrease the balloon’s y-value every time it is called. Our balloon objects also need a speed setting:

import pygame
from pygame.sprite import Sprite
from random import randint

class Balloon(Sprite):

    def __init__(self, screen):

        Sprite.__init__(self)
        self.screen = screen
        self.image = pygame.image.load('green_balloon_50px.png').convert_alpha()
        self.image_w, self.image_h = self.image.get_size()
        self.speed = 0.1

        self.x_position = randint(self.image_w/2, self.screen.get_width()-self.image_w/2)
        self.y_position = self.screen.get_height()

    def update(self, time_passed):
        self.y_position -= self.speed * time_passed

    def blitme(self):
        draw_pos = self.image.get_rect().move(self.x_position-self.image_w/2, self.y_position-self.image_h/2)
        self.screen.blit(self.image, draw_pos)

The main file needs a pygame clock to keep track of time, and a call to the balloon’s update function just before it is drawn in the main event loop:

import pygame
import sys
from Balloon import Balloon

def run_game():
    # screen and game parameters
    screen_width, screen_height = 800, 600
    bg_color = 150, 150, 150

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (screen_width, screen_height), 0, 32)
    clock = pygame.time.Clock()

    # Create our first balloon
    new_balloon = Balloon(screen)

    # main event loop
    while True:
        time_passed = clock.tick(50)

        # Check for game events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        # redraw the screen, every pass through the event loop
        screen.fill(bg_color)

        # Update our balloon, and draw it on the screen
        new_balloon.update(time_passed)
        new_balloon.blitme()

        pygame.display.flip()

run_game()

If you run this code, you will see a balloon start out at a random position at the bottom of the screen, and rise up to the top of the screen. But if we put a print statement into our main game file, we can see that this balloon actually rises forever! To see this, add a print statement just after our call to new_balloon.update:

import pygame
import sys
from Balloon import Balloon

def run_game():
    # screen and game parameters
    screen_width, screen_height = 800, 600
    bg_color = 150, 150, 150

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (screen_width, screen_height), 0, 32)
    clock = pygame.time.Clock()

    # Create our first balloon
    new_balloon = Balloon(screen)

    # main event loop
    while True:
        time_passed = clock.tick(50)

        # Check for game events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        # redraw the screen, every pass through the event loop
        screen.fill(bg_color)

        # Update our balloon, and draw it on the screen
        new_balloon.update(time_passed)
        print "y_position: ", new_balloon.y_position
        new_balloon.blitme()

        pygame.display.flip()

run_game()

The output of this statement shows that the balloon continues to rise past the top of the screen, until our machine runs out of memory:

y_position:  4.9
y_position:  2.9
y_position:  0.9
y_position:  -1.1
y_position:  -3.1
y_position:  -5.1
...
y_position:  -1462.7
y_position:  -1464.7
y_position:  -1466.7
...

Refining our balloons

We are going to fix this by creating a list to store our balloons in. We will also watch our balloon, and remove it from the game when it disappears off the top of the screen. Make the following changes to balloon_ninja.py:

import pygame
import sys
from Balloon import Balloon

def run_game():
    # screen and game parameters
    screen_width, screen_height = 800, 600
    bg_color = 150, 150, 150

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (screen_width, screen_height), 0, 32)
    clock = pygame.time.Clock()

    # Create a list to hold our balloons, and include our first balloon in the list
    balloons = [Balloon(screen)]

    # main event loop
    while True:
        time_passed = clock.tick(50)

        # Check for game events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        # redraw the screen, every pass through the event loop
        screen.fill(bg_color)

        # Update our balloons, and draw them on the screen
        for balloon in balloons:
            balloon.update(time_passed)
            print "y_position", balloon.y_position
            balloon.blitme()
            if balloon.y_position < 0:
                balloons.remove(balloon)

        pygame.display.flip()

run_game()

In lines 15 and 16, we create a list to hold our balloons. When we create this list, we also create our first balloon in the list. In lines 30-36, we loop through our list of balloons. We update each balloon, and use each balloon’s blitme function to redraw it on our screen. We check to see if the balloon has crossed the top of the screen, and if it has we remove it from our list. When we begin implementing our game logic, this is where we will keep track of how many balloons the player has missed. We leave the print statement in for the moment, so we can verify that our balloons are no longer rising to infinite heights in the sky. When you run the program, you should see the y-values stop once they reach 0:

y_position 5.3
y_position 3.3
y_position 1.3
y_position -0.7
$

There is a slight improvement we should make to our balloon’s behavior. If you watch closely you will see that when the balloon first appears on the screen, it does not actually rise from below the screen. It appears with the top half of the balloon immediately visible. This is because the initial y-value of the balloon is equal to the screen height. We need to adjust this initial position down by half of the balloon’s height in Balloon.py:

import pygame
from pygame.sprite import Sprite
from random import randint

class Balloon(Sprite):

    def __init__(self, screen):

        Sprite.__init__(self)
        self.screen = screen
        self.image = pygame.image.load('green_balloon_50px.png').convert_alpha()
        self.image_w, self.image_h = self.image.get_size()
        self.speed = 0.1

        self.x_position = randint(self.image_w/2, self.screen.get_width()-self.image_w/2)
        self.y_position = self.screen.get_height() + self.image_h/2

    def update(self, time_passed):
	    self.y_position -= self.speed * time_passed

    def blitme(self):
        draw_pos = self.image.get_rect().move(self.x_position-self.image_w/2, self.y_position-self.image_h/2)
        self.screen.blit(self.image, draw_pos)

When you run the program now, you should see the top of the balloon start rising from the bottom of the screen. If the balloon is rising too quickly to see this, you can always change the value of self.speed in line 13 to a smaller value, such as 0.01. This would be much too slow for the actual game, but it will let you see the balloon’s initial behavior more clearly.

The same problem happens at the top of the screen. We don’t actually want to remove the balloon from our list until the bottom of the balloon has cleared the top of the screen. This happens when the center of the balloon (it’s y_position) is half the balloon’s height past the top of the screen. Here is the balloon_ninja.py code to do that (we also remove the print statement in this listing):

import pygame
import sys
from Balloon import Balloon

def run_game():
    # screen and game parameters
    screen_width, screen_height = 800, 600
    bg_color = 150, 150, 150

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (screen_width, screen_height), 0, 32)
    clock = pygame.time.Clock()

    # Create a list to hold our balloons, and include our first balloon in the list
    balloons = [Balloon(screen)]

    # main event loop
    while True:
        time_passed = clock.tick(50)

        # Check for game events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        # redraw the screen, every pass through the event loop
        screen.fill(bg_color)

        # Update our balloons, and draw them on the screen
        for balloon in balloons:
            balloon.update(time_passed)
            balloon.blitme()
            if balloon.y_position < -balloon.image_h/2:
                balloons.remove(balloon)

        pygame.display.flip()

run_game()

When you run the program now, you should see a single balloon appear at a random position at the bottom of the screen, and rise smoothly past the top of the screen until it is out of sight:

A balloon rises from below the bottom of the screen, until it disappears off the top of the screen.

A balloon rises from below the bottom of the screen, until it disappears off the top of the screen.

A steady stream of balloons

Now our code is clean enough that we can do some fun things. First let’s modify our balloon_ninja.py file so that as soon as one balloon disappears off the top of the screen, a new balloon starts to rise from the bottom:

import pygame
import sys
from Balloon import Balloon

def run_game():
    # screen and game parameters
    screen_width, screen_height = 800, 600
    bg_color = 150, 150, 150

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (screen_width, screen_height), 0, 32)
    clock = pygame.time.Clock()

    # Create a list to hold our balloons, and include our first balloon in the list
    balloons = [Balloon(screen)]

    # main event loop
    while True:
        time_passed = clock.tick(50)

        # Check for game events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        # redraw the screen, every pass through the event loop
        screen.fill(bg_color)

        # Update our balloons, and draw them on the screen
        for balloon in balloons:
            balloon.update(time_passed)
            balloon.blitme()
            if balloon.y_position < -balloon.image_h/2:
                balloons.remove(balloon)
                balloons.append(Balloon(screen))

        pygame.display.flip()

run_game()

That is a one-line change, that makes our game go on forever! Let’s make it more interesting, so that two balloons are added every time a balloon reaches the top of the screen:

import pygame
import sys
from Balloon import Balloon

def run_game():
    # screen and game parameters
    screen_width, screen_height = 800, 600
    bg_color = 150, 150, 150

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (screen_width, screen_height), 0, 32)
    clock = pygame.time.Clock()

    # Create a list to hold our balloons, and include our first balloon in the list
    balloons = [Balloon(screen)]

    # main event loop
    while True:
        time_passed = clock.tick(50)

        # Check for game events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        # redraw the screen, every pass through the event loop
        screen.fill(bg_color)

        # Update our balloons, and draw them on the screen
        for balloon in balloons:
            balloon.update(time_passed)
            balloon.blitme()
            if balloon.y_position < -balloon.image_h/2:
                balloons.remove(balloon)
                # Create two new balloons every time a balloon disappears!
                for x in range(0,2):
                    balloons.append(Balloon(screen))

        pygame.display.flip()

run_game()

Now you should see more balloons appear every time a balloon reaches the top of the screen:

Every time a balloon disappears off the top of the screen, two new balloons start rising from the bottom of the screen.

Every time a balloon disappears off the top of the screen, two new balloons start rising from the bottom of the screen.

Our game logic will be much more interesting than this. In the next section we will add a sword at the top of the screen, which the player will use to pop balloons. For now, let’s clean up our code so that we are ready to focus on the sword. Our Balloon.py file does not need any modification, because we have the balloon behavior that we want. But let’s remove the spawning behavior in the balloon_ninja.py file:

import pygame
import sys
from Balloon import Balloon

def run_game():
    # screen and game parameters
    screen_width, screen_height = 800, 600
    bg_color = 150, 150, 150

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (screen_width, screen_height), 0, 32)
    clock = pygame.time.Clock()

    # Create a list to hold our balloons, and include our first balloon in the list
    balloons = [Balloon(screen)]

    # main event loop
    while True:
        time_passed = clock.tick(50)

        # Check for game events
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

        # redraw the screen, every pass through the event loop
        screen.fill(bg_color)

        # Update our balloons, and draw them on the screen
        for balloon in balloons:
            balloon.update(time_passed)
            balloon.blitme()
            if balloon.y_position < -balloon.image_h/2:
                balloons.remove(balloon)
                balloons.append(Balloon(screen))

        pygame.display.flip()

run_game()

Next:  Popping Balloons

Back to Tutorial Contents

Advertisements

About ehmatthes

Teacher, hacker, new dad, outdoor guy
This entry was posted in programming and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s