phy 2

Python Bounce Game using Pygame

Python Bounce Game using Pygame

Project Overview

In this advanced project, you'll create a classic bounce game using Pygame, implementing physics, collision detection, scoring mechanisms, and game states. This project will demonstrate your ability to combine various Python programming concepts into a fully functional game.

Learning Objectives

  • Apply object-oriented programming principles to game development
  • Implement physics and collision detection algorithms
  • Create game state management for start, play, and game over screens
  • Develop a scoring system and level progression
  • Handle user input and game events efficiently
  • Optimize game performance using Pygame best practices

Prerequisites

Before starting this project, ensure you have:

  • Python 3.x installed on your system
  • Pygame library installed (install using pip install pygame)
  • Basic understanding of Python control structures, functions, and classes
  • Familiarity with event-driven programming concepts

Tip

It's recommended to use a code editor with Python syntax highlighting such as VSCode, PyCharm, or Thonny for better code organization and debugging capabilities.

Game Concept and Mechanics

The Bounce Game is a classic arcade-style game where the player controls a platform to keep a bouncing ball from falling off the screen. Let's break down the core mechanics:

Core Game Elements

  • Platform: Player-controlled rectangle that moves horizontally to catch the ball
  • Ball: Continuously bouncing object that follows physics-based movement
  • Walls: Screen boundaries that cause the ball to bounce
  • Score System: Points awarded for successful bounces off the platform
  • Lives: Player starts with three lives, losing one when the ball falls off the screen
  • Levels: Difficulty increases with score milestones, affecting ball speed and platform size

User Controls

  • Left/Right arrow keys: Move the platform horizontally
  • Any key: Start the game from the start screen or restart after game over
  • Close window: Exit the game

Project Implementation

1. Setting Up the Game Environment

First, we need to import the necessary libraries and define our constants:

import pygame import sys import random # Constants WIDTH, HEIGHT = 800, 600 BALL_RADIUS = 20 PLATFORM_WIDTH, PLATFORM_HEIGHT = 100, 10 FPS = 60 BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) YELLOW = (255, 255, 0) ORANGE = (255, 165, 0) LIGHT_BLUE = (173, 216, 230) # Initialize Pygame pygame.init() # Create the screen screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Bouncing Ball Game") font = pygame.font.Font(None, 36) # Clock to control the frame rate clock = pygame.time.Clock()

This initialization code sets up our game window, defines colors, dimensions, and creates essential Pygame objects like the display surface and clock for controlling frame rate.

Best Practice

Defining constants at the top of your code makes it easier to adjust game parameters without hunting through the code. This follows the DRY (Don't Repeat Yourself) principle in programming.

2. Creating Game Variables

Next, we'll initialize the variables that will track the state of our game:

# Initialize variables for the game ball_pos = [WIDTH // 2, HEIGHT // 2] ball_speed = [random.uniform(2, 4), random.uniform(2, 4)] platform_pos = [WIDTH // 2 - PLATFORM_WIDTH // 2, HEIGHT - PLATFORM_HEIGHT - 10] platform_speed = 10 score = 0 lives = 3 current_level = 1 platform_color = ORANGE

These variables define the initial state of our game objects:

  • ball_pos: Initial position of the ball (center of screen)
  • ball_speed: Initial velocity vector with random components
  • platform_pos: Initial position of the platform
  • platform_speed: Movement speed of the platform
  • score, lives, current_level: Game state tracking variables
  • platform_color: Visual indicator that changes with levels

3. Implementing Game Screen Functions

We'll create functions to handle different game screens (start, game over, victory):

# Functions for screens def start_screen(): screen.fill(BLACK) show_text_on_screen("Bouncing Ball Game", 50, HEIGHT // 4) show_text_on_screen("Press any key to start...", 30, HEIGHT // 3) show_text_on_screen("Move the platform with arrow keys...", 30, HEIGHT // 2) pygame.display.flip() wait_for_key() def game_over_screen(): screen.fill(BLACK) show_text_on_screen("Game Over", 50, HEIGHT // 3) show_text_on_screen(f"Your final score: {score}", 30, HEIGHT // 2) show_text_on_screen("Press any key to restart...", 20, HEIGHT * 2 // 3) pygame.display.flip() wait_for_key() def victory_screen(): screen.fill(BLACK) show_text_on_screen("Congratulations!", 50, HEIGHT // 3) show_text_on_screen(f"You've won with a score of {score}", 30, HEIGHT // 2) show_text_on_screen("Press any key to exit...", 20, HEIGHT * 2 // 3) pygame.display.flip() wait_for_key() def wait_for_key(): waiting = True while waiting: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() elif event.type == pygame.KEYDOWN: waiting = False def show_text_on_screen(text, font_size, y_position): font = pygame.font.Font(None, font_size) text_render = font.render(text, True, WHITE) text_rect = text_render.get_rect(center=(WIDTH // 2, y_position)) screen.blit(text_render, text_rect) def change_platform_color(): return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

These functions manage different game states:

  • start_screen(): Displays game instructions and waits for player input to start
  • game_over_screen(): Shows the final score when lives are exhausted
  • victory_screen(): Congratulates the player upon achieving a winning condition
  • wait_for_key(): Utility function that pauses execution until a key is pressed
  • show_text_on_screen(): Helper function to render text
  • change_platform_color(): Generates random colors for visual feedback on level changes

Key Concept

This code demonstrates the principle of state management in game development. Different screens represent different states of the game, and transitions between states occur based on player actions or game events.

4. Main Game Loop

The core of any game is its main loop. This is where we handle user input, update game state, and render graphics:

# Main game loop start_screen() game_running = True while game_running: for event in pygame.event.get(): if event.type == pygame.QUIT: game_running = False keys = pygame.key.get_pressed() # Move the platform platform_pos[0] += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * platform_speed # Ensure the platform stays within the screen boundaries platform_pos[0] = max(0, min(platform_pos[0], WIDTH - PLATFORM_WIDTH)) # Move the ball ball_pos[0] += ball_speed[0] ball_pos[1] += ball_speed[1] # Bounce off the walls if ball_pos[0] <= BALL_RADIUS or ball_pos[0] >= WIDTH - BALL_RADIUS: ball_speed[0] = -ball_speed[0] if ball_pos[1] <= BALL_RADIUS: ball_speed[1] = -ball_speed[1] # Check if the ball hits the platform if ( platform_pos[0] <= ball_pos[0] <= platform_pos[0] + PLATFORM_WIDTH and platform_pos[1] - BALL_RADIUS <= ball_pos[1] <= platform_pos[1] + PLATFORM_HEIGHT ): ball_speed[1] = -abs(ball_speed[1]) # Force upward direction score += 1 # Check if the player advances to the next level if score >= current_level * 10: current_level += 1 ball_pos = [WIDTH // 2, HEIGHT // 2] # Increase ball speed with each level ball_speed = [ random.uniform(2, 4) * (1 + current_level * 0.1), random.uniform(2, 4) * (1 + current_level * 0.1) ] platform_color = change_platform_color() # Make platform smaller with each level for increased difficulty PLATFORM_WIDTH = max(40, 100 - (current_level - 1) * 5) # Check if the ball falls off the screen if ball_pos[1] >= HEIGHT: # Decrease lives lives -= 1 if lives == 0: game_over_screen() # Reset game variables score = 0 lives = 3 current_level = 1 PLATFORM_WIDTH = 100 platform_color = ORANGE # Reset the ball position ball_pos = [WIDTH // 2, HEIGHT // 2] # Randomize the ball speed ball_speed = [ random.uniform(2, 4) * (1 + current_level * 0.1), random.uniform(2, 4) * (1 + current_level * 0.1) ] # Clear the screen screen.fill(BLACK) # Draw the ball pygame.draw.circle(screen, WHITE, (int(ball_pos[0]), int(ball_pos[1])), BALL_RADIUS) # Draw the platform pygame.draw.rect( screen, platform_color, (int(platform_pos[0]), int(platform_pos[1]), PLATFORM_WIDTH, PLATFORM_HEIGHT) ) # Display information # Score display score_text = font.render(f"Score: {score}", True, WHITE) score_rect = score_text.get_rect(topleft=(10, 10)) pygame.draw.rect(screen, ORANGE, score_rect.inflate(10, 5)) screen.blit(score_text, score_rect) # Level display level_text = font.render(f"Level: {current_level}", True, WHITE) level_rect = level_text.get_rect(topleft=(score_rect.topright[0] + 50, 10)) pygame.draw.rect(screen, LIGHT_BLUE, level_rect.inflate(10, 5)) screen.blit(level_text, level_rect) # Lives display lives_text = font.render(f"Lives: {lives}", True, WHITE) lives_rect = lives_text.get_rect(topleft=(level_rect.topright[0] + 50, 10)) pygame.draw.rect(screen, RED, lives_rect.inflate(10, 5)) screen.blit(lives_text, lives_rect) # Update the display pygame.display.flip() # Control the frame rate clock.tick(FPS) # Quit Pygame pygame.quit() sys.exit()

Game Loop Breakdown

The main game loop handles several key aspects of the game:

  1. Event Handling: Processes user inputs and window events
  2. Platform Movement: Updates platform position based on arrow key inputs
  3. Ball Physics: Updates ball position and handles bouncing off walls and platform
  4. Collision Detection: Checks for collisions between ball and platform
  5. Scoring & Level Progression: Updates score and advances levels as player hits milestones
  6. Lives Management: Tracks remaining lives and handles game over condition
  7. Rendering: Draws all game elements on screen
  8. Frame Rate Control: Ensures consistent game speed across different systems

Key Concept

The game loop follows the standard pattern of input → update → render that is fundamental to game development. This pattern ensures that the game responds to player input, updates the game state, and displays the results continuously.

5. Advanced Concepts Explained

Collision Detection

Our game implements a simple rectangular collision detection between the ball and platform:

# Check if the ball hits the platform if ( platform_pos[0] <= ball_pos[0] <= platform_pos[0] + PLATFORM_WIDTH and platform_pos[1] - BALL_RADIUS <= ball_pos[1] <= platform_pos[1] + PLATFORM_HEIGHT ): ball_speed[1] = -abs(ball_speed[1]) # Force upward direction score += 1

This checks if the center of the ball is within the horizontal bounds of the platform and if the bottom of the ball is touching the top of the platform. When a collision is detected, the ball's vertical velocity is reversed and made negative (upward) to simulate bouncing.

Physics Simulation

The ball's movement follows basic physics principles:

# Move the ball ball_pos[0] += ball_speed[0] ball_pos[1] += ball_speed[1] # Bounce off the walls if ball_pos[0] <= BALL_RADIUS or ball_pos[0] >= WIDTH - BALL_RADIUS: ball_speed[0] = -ball_speed[0] if ball_pos[1] <= BALL_RADIUS: ball_speed[1] = -ball_speed[1]

The ball's position is updated based on its velocity vector (ball_speed). When the ball hits a boundary, the corresponding velocity component is reversed, creating a bouncing effect. This is a simplified implementation of elastic collision physics.

Difficulty Progression

The game increases in difficulty as the player advances through levels:

# Check if the player advances to the next level if score >= current_level * 10: current_level += 1 ball_pos = [WIDTH // 2, HEIGHT // 2] # Increase ball speed with each level ball_speed = [ random.uniform(2, 4) * (1 + current_level * 0.1), random.uniform(2, 4) * (1 + current_level * 0.1) ] platform_color = change_platform_color() # Make platform smaller with each level for increased difficulty PLATFORM_WIDTH = max(40, 100 - (current_level - 1) * 5)

The difficulty increases in two ways: the ball moves faster (by a factor related to the current level), and the platform becomes smaller, making it harder to catch the ball. The platform color also changes to provide visual feedback of level progression.

Object-Oriented Approach

While our example uses a procedural approach, you can refactor it to use object-oriented programming, which is often better for larger games. Here's how you might define classes for the ball and platform:

class Ball: def __init__(self, x, y, radius, speed): self.x = x self.y = y self.radius = radius self.speed_x, self.speed_y = speed self.color = WHITE def update(self): # Update position self.x += self.speed_x self.y += self.speed_y # Bounce off walls if self.x <= self.radius or self.x >= WIDTH - self.radius: self.speed_x = -self.speed_x if self.y <= self.radius: self.speed_y = -self.speed_y def reset(self, level): # Reset position self.x = WIDTH // 2 self.y = HEIGHT // 2 # Set speed based on level base_speed = random.uniform(2, 4) level_factor = 1 + level * 0.1 self.speed_x = base_speed * level_factor * random.choice([-1, 1]) self.speed_y = base_speed * level_factor def draw(self, screen): pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), self.radius) def is_colliding_with_platform(self, platform): return ( platform.x <= self.x <= platform.x + platform.width and platform.y - self.radius <= self.y <= platform.y + platform.height ) def is_out_of_bounds(self): return self.y >= HEIGHT class Platform: def __init__(self, x, y, width, height, speed): self.x = x self.y = y self.width = width self.height = height self.speed = speed self.color = ORANGE def update(self, keys): # Move based on key presses if keys[pygame.K_LEFT]: self.x -= self.speed if keys[pygame.K_RIGHT]: self.x += self.speed # Keep within screen bounds self.x = max(0, min(self.x, WIDTH - self.width)) def draw(self, screen): pygame.draw.rect( screen, self.color, (int(self.x), int(self.y), self.width, self.height) ) def set_color(self, color): self.color = color def set_width(self, width): self.width = width

With these classes, the main game loop would be cleaner and more organized:

# Create game objects ball = Ball(WIDTH // 2, HEIGHT // 2, BALL_RADIUS, [random.uniform(2, 4), random.uniform(2, 4)]) platform = Platform( WIDTH // 2 - PLATFORM_WIDTH // 2, HEIGHT - PLATFORM_HEIGHT - 10, PLATFORM_WIDTH, PLATFORM_HEIGHT, 10 ) # Game variables score = 0 lives = 3 current_level = 1 # Main game loop while game_running: # Handle events for event in pygame.event.get(): if event.type == pygame.QUIT: game_running = False keys = pygame.key.get_pressed() # Update game objects platform.update(keys) ball.update() # Check for collisions if ball.is_colliding_with_platform(platform): ball.speed_y = -abs(ball.speed_y) # Bounce up score += 1 # Check for level advancement if score >= current_level * 10: current_level += 1 ball.reset(current_level) platform.set_color(change_platform_color()) platform.set_width(max(40, 100 - (current_level - 1) * 5)) # Check if ball fell off screen if ball.is_out_of_bounds(): lives -= 1 if lives == 0: game_over_screen() # Reset game score = 0 lives = 3 current_level = 1 platform.set_width(100) platform.set_color(ORANGE) ball.reset(current_level) # Render screen.fill(BLACK) ball.draw(screen) platform.draw(screen) # Display HUD (score, level, lives) # ... (code for displaying text) pygame.display.flip() clock.tick(FPS)

Key Concept

The object-oriented approach encapsulates data and behavior within classes, making the code more organized, reusable, and extensible. This is especially valuable for larger games with many interacting components.

Extension Challenges

Once you've implemented the basic game, try these challenges to enhance your skills:

Challenge 1: Power-ups

Implement power-ups that occasionally fall from the top of the screen. Ideas include:

  • Extra life power-up
  • Platform size increase
  • Slow ball speed temporarily
  • Multi-ball (add another ball to the game)

Challenge 2: Obstacles

Add breakable or non-breakable obstacles to the game area that the ball can bounce off of. Implement collision physics for these obstacles.

Challenge 3: Realistic Physics

Enhance the ball's physics by implementing:

  • Gravity effects (constant downward acceleration)
  • Ball spin based on where it hits the platform
  • Momentum transfer from the moving platform to the ball

Challenge 4: Sound Effects and Music

Add audio feedback to the game using Pygame's sound capabilities:

  • Bouncing sounds
  • Level advancement jingle
  • Game over sound
  • Background music that changes with levels

Challenge 5: Visual Polish

Enhance the visual aspects of the game:

  • Add particle effects when the ball bounces
  • Implement ball trails
  • Create animated backgrounds
  • Add a visual countdown when starting a new life

Debugging Tips

  • Visual debugging: Draw temporary lines or shapes to visualize collision areas or trajectories
  • Print statements: Output variable values at key points to understand the game state
  • Slow motion: Temporarily reduce the FPS to observe complex interactions more clearly
  • Isolation testing: Test individual components (like collision detection) in a simplified environment
  • State logging: Create a logging system that records game state at regular intervals
# Example of visual debugging def debug_draw(): # Draw platform collision area pygame.draw.rect( screen, (255, 0, 0, 128), # Red with transparency (int(platform_pos[0]), int(platform_pos[1]), PLATFORM_WIDTH, PLATFORM_HEIGHT), 1 # Width of outline ) # Draw ball velocity vector line_end_x = ball_pos[0] + ball_speed[0] * 10 line_end_y = ball_pos[1] + ball_speed[1] * 10 pygame.draw.line( screen, (0, 255, 0), # Green (int(ball_pos[0]), int(ball_pos[1])), (int(line_end_x), int(line_end_y)), 2 # Line width ) # Call this in your render section when debugging # debug_draw()

Performance Optimization

As your game grows more complex, consider these optimization techniques:

  1. Use sprite groups for efficient rendering and collision detection
  2. Implement object pooling for frequently created/destroyed objects
  3. Optimize collision detection with spatial partitioning techniques
  4. Limit screen updates to changed areas rather than redrawing everything
  5. Profile your code to identify bottlenecks using tools like cProfile

Tip

Use Pygame's built-in sprite and sprite group classes for efficient rendering and collision detection when your game has many objects:

pygame.sprite.Sprite and pygame.sprite.Group

Conclusion

This Python Bounce Game project demonstrates how to combine multiple programming concepts into a complete game application. By implementing this project, you've gained experience with:

  • Creating and managing a game loop
  • Handling user input and events
  • Implementing physics and collision detection
  • Managing game state and progression
  • Building a user interface with score display
  • Using object-oriented design principles in game development

These skills form a solid foundation for more complex game development projects. You can continue to enhance this game with the extension challenges or apply your knowledge to create entirely new games with Pygame.

Resources for Further Learning

Post a Comment

0 Comments