Title: Make a ball popping game in Python and Pygame
The example Make a game framework with Python and Pygame shows how to make a simple bouncing ball animation. It's a game framework but not really a game because it doesn't interact with the user.
This example lets you click on balls to pop them. It also plays some sounds.
To make the example work, I modified the BallSprite class and the main app class, which is now called PopperApp.
BallSprite Changes
When the user clicks on the program, the main program needs to determine whether the mouse click hits a ball. To do that, the BallSprite class provides the following is_at function.
def is_at(self, position):
'''Return True if this point is inside the ball.'''
# See if we are alive.
if not self.is_alive:
# We're dead so we don't respond to mouse clicks.
return False
else:
# We're alive. It's a hit if it is
# within self.radius of our center.
return math.dist(self.center, position) < self.radius
The code first checks the sprite's new is_alive value. If the sprite is no longer alive, it should not respond to mouse events so the function returns False.
If the sprite is still alive, the code uses math.dist to calculate the distance between the ball's center and the mouse position. If that distance is less than the ball's radius, it returns True. Otherwise it returns False.
When the user clicks on a ball, the main program calls the following sprite pop method to make it pop.
def pop(self):
'''Mark this ball as popped.'''
self.is_alive = False
This code simply marks the ball as no longer alive so it ignores further mouse clicks and it can be removed.
The last change to the BallSprite class is in its update method. In the earlier example, that method updates the balls position and returns True to indicate that the sprite is still alive. In this example, the method works exactly the same way except it ends with the following code.
# Return True if we are still alive.
return self.is_alive
Now the method returns True if the sprite is still alive and False otherwise so the main event loop can remove the sprite from its sprite list.
PopperApp Changes
The PopperApp class includes a few changes.
First, when it starts, it uses the following code to load several sound files.
# Prepare sounds.
mixer = pygame.mixer
mixer.init()
self.boing_sound = pygame.mixer.Sound('boing.wav')
# https://freesound.org/people/Reitanna/sounds/323740/
self.pop_sound = pygame.mixer.Sound('pop.wav')
# https://freesound.org/people/Krokulator/sounds/652615/
self.miss_sound = pygame.mixer.Sound('miss.wav')
# https://freesound.org/people/plasterbrain/sounds/397354/
self.won_sound = pygame.mixer.Sound('tada.flac')
# https://freesound.org/people/NikPlaymostories/sounds/563850/
self.lost_sound = pygame.mixer.Sound('fail.mp3')
The program's event loop contains a few changes. The following code shows the event_loop method with the changes highlighted in blue.
# Process events.
def event_loop(self):
clock = pygame.time.Clock()
player_won = False
running = True
while running:
# Event loop.
for event in pygame.event.get():
# Quit.
if event.type == pygame.locals.QUIT:
running = False
break
# Mouse down.
if event.type == pygame.MOUSEBUTTONDOWN:
# Get the mouse position.
position = pygame.mouse.get_pos()
# See if any ball is at this position.
had_hit = False
for sprite in self.all_sprites:
if sprite.is_at(position):
# Pop it.
had_hit = True
sprite.pop()
self.pop_sound.play()
break
# If we hit mothing, play the miss sound.
if not had_hit:
self.miss_sound.play()
# See how much time has passed since the last update.
elapsed_ticks = clock.tick(self.max_fps)
elapsed_seconds = elapsed_ticks / 1000
# Update the sprites.
self.update_sprites(elapsed_seconds)
# Draw the sprites.
self.draw_sprites()
# Update the display.
pygame.display.update()
# If all sprites are gone, we're done.
if len(self.all_sprites) == 0:
player_won = True
running= False
# See if the player won.
if player_won:
# The player won. Play the won sound.
channel = self.won_sound.play()
else:
# The player lost. Play the lost sound.
channel = self.lost_sound.play()
# Wait for the final sound to finish.
while channel.get_busy():
pygame.time.wait(100)
# End the program.
pygame.display.quit()
pygame.quit()
The method first sets player_won = False so it can determine whether the player has won.
The event processing loop checks for QUIT messages as before. It then looks for MOUSEBUTTONDOWN messages. If the user pressed the mouse down, te code gets the mouse's position. It then loops through the sprites and calls their is_at methods to see if any of them is at the mouse's position.
If a sprite has been hit, the code sets had_hit to True to remember that a sprite was hit. It calls the sprite's pop method so it knows that it has been popped. It plays the pop sound and breaks out of the loop so a single mouse click can only pop one sprite.
After the code updates the sprites, it checks whether all of the sprites have been removed from the self.all_sprites list. If all of the sprites have been popped, the code sets running to False to end the event processing loop.
After the event processing loop ends, the code checks player_won. If the player won, the code plays the won sound. If the player did not win, the program plays the lost sound. After playing one sound or the other, the program loops until the sound finishes playing before ending.
Conclusion
In this example, the main program detects mouse click. When it detects a click, it calls the sprite class to determine whether it was clicked. If it was, the main program tells the sprite that it was popped. The sprite's update method returns False and the main program removes the sprite from its sprite list.
When the program ends, it plays either the win or lost sound and waits for that sound to finish before closing.
There are many changes that you could make to turn this into a fancier game such as:
- Let the user play again rather than closing.
- Keep score somehow. For example, count the number of seconds required to pop all of the balls. Or count hits and misses.
- Provide levels. For example, after popping all balls, start again with more balls moving faster. The game ends if the use doesn't pop them all within a certain amount of time.
- Use your imagination!
Download the example to experiment with it and to see additional details.
|