Master complex physics with particle systems and advanced mechanics!
Create amazing visual effects with hundreds of particles that follow physics laws:
import pygame
import random
import math
import sys
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Particle Explosion!")
clock = pygame.time.Clock()
class Particle:
def __init__(self, x, y):
self.x = x
self.y = y
angle = random.uniform(0, 2 * math.pi)
speed = random.uniform(2, 8)
self.velocity_x = math.cos(angle) * speed
self.velocity_y = math.sin(angle) * speed
self.gravity = 0.2
self.life = 60
self.color = [random.randint(200, 255), random.randint(100, 200), random.randint(50, 150)]
def update(self):
self.velocity_y += self.gravity
self.x += self.velocity_x
self.y += self.velocity_y
self.life -= 1
# Fade color as particle ages
for i in range(3):
self.color[i] = max(0, self.color[i] - 3)
def draw(self, screen):
if self.life > 0:
pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), max(1, self.life // 10))
# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 100, 100)
particles = []
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
# Create explosion at mouse position
mouse_x, mouse_y = pygame.mouse.get_pos()
for _ in range(50): # Create 50 particles
particles.append(Particle(mouse_x, mouse_y))
# Update particles
for particle in particles[:]:
particle.update()
if particle.life <= 0:
particles.remove(particle)
# Draw everything
screen.fill(BLACK)
for particle in particles:
particle.draw(screen)
# Instructions
font = pygame.font.Font(None, 36)
text = font.render("Click anywhere to create explosions!", True, WHITE)
screen.blit(text, (10, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit() Physics Concepts: This particle system demonstrates gravity, velocity, and life cycles. Each particle starts with random velocity and gradually falls due to gravity while fading away.
Build a complete physics-based puzzle game where players aim and shoot a ball to hit targets:
import pygame
import math
import sys
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Physics Puzzle Game!")
clock = pygame.time.Clock()
# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (100, 150, 255)
RED = (255, 100, 100)
GREEN = (100, 255, 100)
BROWN = (139, 69, 19)
class Ball:
def __init__(self, x, y):
self.x = x
self.y = y
self.radius = 15
self.velocity_x = 0
self.velocity_y = 0
self.gravity = 0.5
self.friction = 0.99
self.bounce = 0.8
self.dragging = False
def update(self, obstacles):
# Apply gravity
self.velocity_y += self.gravity
# Apply friction
self.velocity_x *= self.friction
self.velocity_y *= self.friction
# Update position
self.x += self.velocity_x
self.y += self.velocity_y
# Check collisions with obstacles
for obstacle in obstacles:
if self.check_collision(obstacle):
self.resolve_collision(obstacle)
# Bounce off walls
if self.x - self.radius <= 0 or self.x + self.radius >= 800:
self.velocity_x = -self.velocity_x * self.bounce
self.x = max(self.radius, min(800 - self.radius, self.x))
# Bounce off ground and ceiling
if self.y - self.radius <= 0 or self.y + self.radius >= 600:
self.velocity_y = -self.velocity_y * self.bounce
self.y = max(self.radius, min(600 - self.radius, self.y))
def check_collision(self, obstacle):
closest_x = max(obstacle['x'], min(self.x, obstacle['x'] + obstacle['width']))
closest_y = max(obstacle['y'], min(self.y, obstacle['y'] + obstacle['height']))
distance = math.sqrt((self.x - closest_x)**2 + (self.y - closest_y)**2)
return distance < self.radius
def resolve_collision(self, obstacle):
# Simple collision response
center_x = obstacle['x'] + obstacle['width'] / 2
center_y = obstacle['y'] + obstacle['height'] / 2
# Push ball away from obstacle center
dx = self.x - center_x
dy = self.y - center_y
distance = math.sqrt(dx*dx + dy*dy)
if distance > 0:
# Normalize and apply bounce
dx /= distance
dy /= distance
self.velocity_x = dx * 5
self.velocity_y = dy * 5
def draw(self, screen):
pygame.draw.circle(screen, BLUE, (int(self.x), int(self.y)), self.radius)
pygame.draw.circle(screen, WHITE, (int(self.x - 5), int(self.y - 5)), 3)
class Target:
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.hit = False
def check_hit(self, ball):
if (ball.x - ball.radius < self.x + self.width and
ball.x + ball.radius > self.x and
ball.y - ball.radius < self.y + self.height and
ball.y + ball.radius > self.y):
self.hit = True
def draw(self, screen):
color = GREEN if self.hit else RED
pygame.draw.rect(screen, color, (self.x, self.y, self.width, self.height))
# Create obstacles
obstacles = [
{'x': 200, 'y': 300, 'width': 100, 'height': 20},
{'x': 400, 'y': 200, 'width': 20, 'height': 100},
{'x': 500, 'y': 400, 'width': 150, 'height': 20},
{'x': 100, 'y': 500, 'width': 200, 'height': 20},
]
# Create target and ball
target = Target(650, 100, 50, 50)
ball = Ball(50, 50)
mouse_start = None
level_complete = False
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if not level_complete:
mouse_start = pygame.mouse.get_pos()
ball.dragging = True
elif event.type == pygame.MOUSEBUTTONUP:
if ball.dragging and mouse_start:
mouse_x, mouse_y = pygame.mouse.get_pos()
power = min(math.sqrt((mouse_start[0] - mouse_x)**2 + (mouse_start[1] - mouse_y)**2), 100)
angle = math.atan2(mouse_y - mouse_start[1], mouse_x - mouse_start[0])
ball.velocity_x = math.cos(angle) * power * 0.2
ball.velocity_y = math.sin(angle) * power * 0.2
ball.dragging = False
mouse_start = None
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
# Reset level
ball = Ball(50, 50)
target.hit = False
level_complete = False
# Update game
if not level_complete:
ball.update(obstacles)
target.check_hit(ball)
if target.hit:
level_complete = True
# Draw everything
screen.fill(BLACK)
# Draw obstacles
for obstacle in obstacles:
pygame.draw.rect(screen, BROWN, (obstacle['x'], obstacle['y'], obstacle['width'], obstacle['height']))
# Draw target
target.draw(screen)
# Draw ball
ball.draw(screen)
# Draw aim line when dragging
if ball.dragging and mouse_start:
mouse_x, mouse_y = pygame.mouse.get_pos()
pygame.draw.line(screen, WHITE, mouse_start, (mouse_x, mouse_y), 2)
# Instructions
font = pygame.font.Font(None, 36)
if level_complete:
text = font.render("Level Complete! Press R to restart", True, GREEN)
else:
text = font.render("Click and drag to aim the ball!", True, WHITE)
screen.blit(text, (10, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit() Game Mechanics: This puzzle game combines aiming, projectile physics, collision detection, and win conditions. Players must use physics to navigate obstacles and hit the target.
Here's a classic ball and paddle game using tkinter. This version includes improved keyboard handling for Spyder:
import tkinter as tk
import random
import time
class Ball:
def __init__(self, canvas, paddle, color):
self.canvas = canvas
self.paddle = paddle
self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
self.canvas.move(self.id, 245, 100)
starts = [-3, -2, -1, 1, 2, 3]
random.shuffle(starts)
self.x = starts[0]
self.y = -3
self.canvas_height = self.canvas.winfo_height()
self.canvas_width = self.canvas.winfo_width()
self.hit_bottom = False
def hit_paddle(self, pos):
paddle_pos = self.canvas.coords(self.paddle.id)
# pos = [x1, y1, x2, y2] for the ball
# paddle_pos = [x1, y1, x2, y2] for the paddle
if pos[2] >= paddle_pos[0] and pos[0] <= paddle_pos[2]:
if pos[3] >= paddle_pos[1] and pos[3] <= paddle_pos[3]:
return True
return False
def draw(self):
self.canvas.move(self.id, self.x, self.y)
pos = self.canvas.coords(self.id)
if pos[1] <= 0:
self.y = 3
if pos[3] >= self.canvas_height:
self.hit_bottom = True
if self.hit_paddle(pos):
self.y = -3
if pos[0] <= 0:
self.x = 3
if pos[2] >= self.canvas_width:
self.x = -3
class Paddle:
def __init__(self, root, canvas, color):
self.canvas = canvas
self.id = canvas.create_rectangle(0, 0, 100, 10, fill=color)
self.canvas.move(self.id, 200, 300)
self.x = 0
self.canvas_width = self.canvas.winfo_width()
self.left_pressed = False
self.right_pressed = False
# Bind arrow keys to root and canvas for better Spyder compatibility
root.bind('<Left>', self.turn_left)
root.bind('<Right>', self.turn_right)
root.bind('<KeyRelease-Left>', self.stop_move)
root.bind('<KeyRelease-Right>', self.stop_move)
# Also bind to canvas
canvas.bind('<Left>', self.turn_left)
canvas.bind('<Right>', self.turn_right)
canvas.bind('<KeyRelease-Left>', self.stop_move)
canvas.bind('<KeyRelease-Right>', self.stop_move)
# Make canvas focusable
canvas.focus_set()
def draw(self):
# Continuous movement based on key states
if self.left_pressed:
self.x = -3
elif self.right_pressed:
self.x = 3
else:
self.x = 0
self.canvas.move(self.id, self.x, 0)
pos = self.canvas.coords(self.id)
if pos[0] <= 0:
self.canvas.coords(self.id, 0, pos[1], 100, pos[3])
elif pos[2] >= self.canvas_width:
self.canvas.coords(self.id, self.canvas_width-100, pos[1], self.canvas_width, pos[3])
def turn_left(self, event):
self.left_pressed = True
self.right_pressed = False
def turn_right(self, event):
self.right_pressed = True
self.left_pressed = False
def stop_move(self, event):
if event.keysym == 'Left':
self.left_pressed = False
elif event.keysym == 'Right':
self.right_pressed = False
def main():
root = tk.Tk()
root.title("Ball & Paddle Game (Spyder Compatible)")
root.resizable(0, 0)
root.wm_attributes("-topmost", 1)
canvas = tk.Canvas(root, width=500, height=400, bd=0, highlightthickness=0, bg='black')
canvas.pack()
# Important: update before getting width/height and set focus
root.update()
canvas.focus_set()
# Add instructions
instructions = canvas.create_text(250, 50, text="Use Arrow Keys to Move Paddle",
fill='white', font=('Arial', 12))
paddle = Paddle(root, canvas, 'blue')
ball = Ball(canvas, paddle, 'red')
# Main game loop
while True:
if not ball.hit_bottom:
ball.draw()
paddle.draw()
root.update_idletasks()
root.update()
time.sleep(0.01)
else:
# Game over
canvas.create_text(250, 200, text="Game Over!",
font=('Arial', 24), fill='red')
canvas.create_text(250, 230, text="Close window to exit",
font=('Arial', 12), fill='white')
break
# Keep window open after game over
root.mainloop()
if __name__ == "__main__":
main() Spyder Compatibility: This version includes improved keyboard handling for Spyder. The paddle uses continuous movement tracking and binds keys to both root and canvas for better responsiveness in IDE environments.
Tkinter vs Pygame: While tkinter comes built-in with Python, pygame offers better game development features. Tkinter is great for simple games and learning, but pygame provides smoother animation and better input handling for complex games.
Now it's time to create your own physics-based game! Here are some ideas to get you started:
Ready for the final lesson?
Next: Mathematical Art →