Skip to content

Instantly share code, notes, and snippets.

@gary149
Last active April 24, 2025 22:11
Show Gist options
  • Select an option

  • Save gary149/4ad09c24b262a782c8688543b7cf1155 to your computer and use it in GitHub Desktop.

Select an option

Save gary149/4ad09c24b262a782c8688543b7cf1155 to your computer and use it in GitHub Desktop.
import pygame
import sys
import math
import numpy as np
# Initialize pygame
pygame.init()
# Constants
WIDTH, HEIGHT = 800, 600
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
# Physics constants
GRAVITY = 0.5
AIR_RESISTANCE = 0.995
FRICTION = 0.6
RESTITUTION = 0.85
class Ball:
def __init__(self, x, y, radius=15):
self.x = x
self.y = y
self.radius = radius
self.vx = 2
self.vy = 0
self.mass = radius * 0.5
def update(self):
# Apply gravity
self.vy += GRAVITY
# Apply air resistance
self.vx *= AIR_RESISTANCE
self.vy *= AIR_RESISTANCE
# Update position
self.x += self.vx
self.y += self.vy
def draw(self, screen):
pygame.draw.circle(screen, RED, (int(self.x), int(self.y)), self.radius)
class Hexagon:
def __init__(self, center_x, center_y, radius=200):
self.center_x = center_x
self.center_y = center_y
self.radius = radius
self.rotation_speed = 0.01 # radians per frame
self.angle = 0
self.vertices = []
self.update_vertices()
def update(self):
self.angle += self.rotation_speed
self.update_vertices()
def update_vertices(self):
self.vertices = []
for i in range(6):
angle = self.angle + i * (2 * math.pi / 6)
x = self.center_x + self.radius * math.cos(angle)
y = self.center_y + self.radius * math.sin(angle)
self.vertices.append((x, y))
def draw(self, screen):
pygame.draw.polygon(screen, BLUE, self.vertices, 2)
def check_collision(ball, hexagon):
# Check collision with each edge of the hexagon
for i in range(6):
p1 = hexagon.vertices[i]
p2 = hexagon.vertices[(i + 1) % 6]
# Vector from p1 to p2
edge_vector = (p2[0] - p1[0], p2[1] - p1[1])
edge_length = math.sqrt(edge_vector[0] ** 2 + edge_vector[1] ** 2)
# Normalized edge vector
if edge_length > 0:
edge_normal = (edge_vector[0] / edge_length, edge_vector[1] / edge_length)
else:
continue
# Vector from p1 to ball center
to_ball = (ball.x - p1[0], ball.y - p1[1])
# Project to_ball onto the edge
projection_length = to_ball[0] * edge_normal[0] + to_ball[1] * edge_normal[1]
# Clamp projection to edge length
projection_length = max(0, min(edge_length, projection_length))
# Find closest point on edge to ball
closest_point = (
p1[0] + projection_length * edge_normal[0],
p1[1] + projection_length * edge_normal[1],
)
# Distance from ball to closest point
dx = ball.x - closest_point[0]
dy = ball.y - closest_point[1]
distance = math.sqrt(dx**2 + dy**2)
# Check if collision occurred
if distance <= ball.radius:
# Calculate normal vector (perpendicular to edge)
normal = (-edge_normal[1], edge_normal[0])
# Make sure normal points toward the ball
dot_product = dx * normal[0] + dy * normal[1]
if dot_product < 0:
normal = (-normal[0], -normal[1])
# Calculate relative velocity of ball to wall
# For a rotating hexagon, we need to consider the velocity of the wall at the contact point
wall_vx = -hexagon.rotation_speed * (closest_point[1] - hexagon.center_y)
wall_vy = hexagon.rotation_speed * (closest_point[0] - hexagon.center_x)
rel_vx = ball.vx - wall_vx
rel_vy = ball.vy - wall_vy
# Calculate velocity component along the normal
vel_along_normal = rel_vx * normal[0] + rel_vy * normal[1]
# Only resolve collision if objects are moving toward each other
if vel_along_normal < 0:
# Calculate impulse scalar
j = -(1 + RESTITUTION) * vel_along_normal
# Apply impulse to ball's velocity
ball.vx += j * normal[0]
ball.vy += j * normal[1]
# Apply friction to the tangential component
tangent = (-normal[1], normal[0])
vel_along_tangent = rel_vx * tangent[0] + rel_vy * tangent[1]
ball.vx -= (1 - FRICTION) * vel_along_tangent * tangent[0]
ball.vy -= (1 - FRICTION) * vel_along_tangent * tangent[1]
# Move ball outside of wall to prevent sticking
penetration_depth = ball.radius - distance
ball.x += penetration_depth * normal[0]
ball.y += penetration_depth * normal[1]
return True
return False
def main():
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Ball Bouncing in a Spinning Hexagon")
clock = pygame.time.Clock()
ball = Ball(WIDTH // 2, HEIGHT // 3)
hexagon = Hexagon(WIDTH // 2, HEIGHT // 2)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Update physics
ball.update()
hexagon.update()
# Check for collisions
check_collision(ball, hexagon)
# Draw everything
screen.fill(BLACK)
hexagon.draw(screen)
ball.draw(screen)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment