I'm attempting to model the bouncing of balls in a pattern in python with pygame. Something is causing the physics to be incorrect - the balls GAIN energy, i.e. they bounce fractionally higher over time. (I have included a 'speed factor' which can be increase to see the effect I describe.)
Here is my code:
import pygame
import math
# Window dimensions
WIDTH = 800
HEIGHT = 600
# Circle properties
RADIUS = 5
NUM_BALLS = 20
GRAVITY = 9.81 # Gravitational acceleration in m/s²
SPEED_FACTOR = 10 # Speed multiplier for animation
# Circle class
class Circle:
def __init__(self, x, y, vx, vy, color):
self.x = x
self.y = y
self.vx = vx
self.vy = vy
self.color = color
def update(self, dt):
# Update positions
self.x += self.vx * dt
self.y += self.vy * dt
# Apply gravity
self.vy += GRAVITY * dt
# Bounce off walls
if self.x - RADIUS < 0 or self.x + RADIUS > WIDTH:
self.vx *= -1
self.x = max(RADIUS, min(WIDTH - RADIUS, self.x)) # Clamp x within bounds
if self.y - RADIUS < 0 or self.y + RADIUS > HEIGHT:
self.vy *= -1
self.y = max(RADIUS, min(HEIGHT - RADIUS, self.y)) # Clamp y within bounds
def draw(self, screen):
pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), RADIUS)
# Initialize Pygame
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
circles = []
# Calculate circle arrangement
circle_radius = RADIUS * 2 # Diameter of an individual ball
circle_diameter = NUM_BALLS * circle_radius # Diameter of the circle arrangement
circle_center_x = WIDTH // 2
circle_center_y = HEIGHT // 2
angle_increment = 2 * math.pi / NUM_BALLS
for i in range(NUM_BALLS):
angle = i * angle_increment
x = circle_center_x + math.cos(angle) * circle_diameter / 2
y = circle_center_y + math.sin(angle) * circle_diameter / 2
vx = 0
vy = 0
hue = i * (360 // NUM_BALLS) # Calculate hue value based on the number of balls
color = pygame.Color(0)
color.hsla = (hue, 100, 50, 100) # Set color using HSL color space
circles.append(Circle(x, y, vx, vy, color))
# Game loop
running = True
clock = pygame.time.Clock()
prev_time = pygame.time.get_ticks() # Previous frame time
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
current_time = pygame.time.get_ticks()
dt = (current_time - prev_time) / 1000.0 # Time elapsed in seconds since the last frame
dt *= SPEED_FACTOR # Multiply dt by speed factor
prev_time = current_time # Update previous frame time
screen.fill((0, 0, 0)) # Clear the screen
for circle in circles:
circle.update(dt)
circle.draw(screen)
pygame.display.flip() # Update the screen
pygame.quit()
I don't fully understand how the gravity factor works, but assume it's just an acceleration. Where is the extra energy coming in to the system?