1
+ import pygame
2
+ import pmma
3
+ import random
4
+ import math
5
+ import time
6
+
7
+ pygame .init ()
8
+ pmma .init ()
9
+
10
+ display = pygame .display .set_mode ((1920 , 1080 ), pygame .FULLSCREEN )
11
+ clock = pygame .time .Clock ()
12
+
13
+ GRAVITY = 0.5 # Acceleration due to gravity
14
+ FRICTION = 0.9 # Energy retention on wall bounces
15
+ EXPLOSION_SPEED = 15 # Speed of explosion
16
+
17
+ class BouncyBall :
18
+ def __init__ (self , x , y , angle ):
19
+ self .x = x
20
+ self .y = y
21
+ self .radius = 10
22
+ self .vx = math .cos (angle ) * EXPLOSION_SPEED # Velocity in x direction
23
+ self .vy = math .sin (angle ) * EXPLOSION_SPEED # Velocity in y direction
24
+ color = pmma .ColorConverter ()
25
+ self .color = color .generate_color_from_perlin_noise ()
26
+ self .start_time = time .perf_counter ()
27
+
28
+ def render (self ):
29
+ pygame .draw .circle (display , self .color , (int (self .x ), int (self .y )), self .radius )
30
+
31
+ def compute (self , balls ):
32
+ # Apply gravity
33
+ self .vy += GRAVITY
34
+
35
+ # Update position
36
+ self .x += self .vx
37
+ self .y += self .vy
38
+
39
+ # Wall collisions
40
+ if self .x - self .radius < 0 : # Left wall
41
+ self .x = self .radius
42
+ self .vx = - self .vx * FRICTION
43
+ elif self .x + self .radius > display .get_width (): # Right wall
44
+ self .x = display .get_width () - self .radius
45
+ self .vx = - self .vx * FRICTION
46
+
47
+ if self .y - self .radius < 0 : # Ceiling
48
+ self .y = self .radius
49
+ self .vy = - self .vy * FRICTION
50
+ elif self .y + self .radius > display .get_height (): # Floor
51
+ self .y = display .get_height () - self .radius
52
+ self .vy = - self .vy * FRICTION
53
+
54
+ if (time .perf_counter () - self .start_time ) < 1 :
55
+ return False
56
+ # Check collisions with other balls
57
+ for other in balls :
58
+ if other is not self :
59
+ self .check_collision (other )
60
+
61
+ if abs (self .vy ) < 0.237 and int (self .y ) == display .get_height () - self .radius :
62
+ return True
63
+ return False
64
+
65
+ def check_collision (self , other ):
66
+ # Vector between ball centers
67
+ dx = self .x - other .x
68
+ dy = self .y - other .y
69
+ distance = math .sqrt (dx ** 2 + dy ** 2 )
70
+
71
+ # Check if balls are overlapping
72
+ if distance < self .radius + other .radius :
73
+ # Resolve collision
74
+ angle = math .atan2 (dy , dx )
75
+
76
+ # Velocity components along the collision axis
77
+ self_vx = self .vx * math .cos (angle ) + self .vy * math .sin (angle )
78
+ other_vx = other .vx * math .cos (angle ) + other .vy * math .sin (angle )
79
+
80
+ # Exchange velocities
81
+ self .vx , other .vx = (
82
+ other_vx * math .cos (angle ) - self_vx * math .sin (angle ),
83
+ self_vx * math .cos (angle ) - other_vx * math .sin (angle ),
84
+ )
85
+
86
+ # Resolve overlap
87
+ overlap = (self .radius + other .radius - distance ) / 2
88
+ self .x += overlap * math .cos (angle )
89
+ self .y += overlap * math .sin (angle )
90
+ other .x -= overlap * math .cos (angle )
91
+ other .y -= overlap * math .sin (angle )
92
+
93
+
94
+ def create_explosion (center_x , center_y , num_balls ):
95
+ """Create balls exploding outward from the center."""
96
+ balls = []
97
+ for i in range (num_balls ):
98
+ angle = i * (2 * math .pi / num_balls ) # Evenly spaced angles
99
+ balls .append (BouncyBall (center_x , center_y , angle ))
100
+ return balls
101
+
102
+
103
+ # Initialize explosion
104
+ number_of_balls = random .randint (10 , 120 )
105
+ balls = create_explosion (display .get_width () / 2 , display .get_height () / 2 , number_of_balls )
106
+
107
+ surface = pygame .Surface ((display .get_width (), display .get_height ()))
108
+ # Main loop
109
+ while True :
110
+ for event in pygame .event .get ():
111
+ if event .type == pygame .QUIT :
112
+ pygame .quit ()
113
+ quit ()
114
+
115
+ surface .blit (display , (0 , 0 ))
116
+ display .fill ([0 , 0 , 0 ]) # Clear screen
117
+ surface .set_alpha (200 )
118
+ display .blit (surface , (0 , 0 ))
119
+
120
+ index = 0
121
+ while index < len (balls ):
122
+ ball = balls [index ]
123
+ if ball .compute (balls ):
124
+ balls .remove (ball )
125
+ else :
126
+ ball .render ()
127
+ index += 1
128
+
129
+ if len (balls ) == 0 :
130
+ number_of_balls = random .randint (10 , 120 )
131
+ balls = create_explosion (display .get_width () / 2 , display .get_height () / 2 , number_of_balls )
132
+
133
+ pygame .display .flip ()
134
+ clock .tick (60 )
0 commit comments