nitus-ac commited on
Commit
0fb5b0f
·
verified ·
1 Parent(s): 2b7a870

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +232 -0
app.py ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pygame
2
+ import random
3
+ import math
4
+
5
+ # --- Game Constants ---
6
+ WIDTH, HEIGHT = 800, 600
7
+ FPS = 60
8
+ WHITE = (255, 255, 255)
9
+ BLACK = (0, 0, 0)
10
+ SHIP_THRUST = 0.2
11
+ SHIP_MAX_SPEED = 5
12
+ BULLET_SPEED = 7
13
+
14
+ # --- Pygame Initialization ---
15
+ pygame.init()
16
+ screen = pygame.display.set_mode((WIDTH, HEIGHT))
17
+ pygame.display.set_caption("Pygame Asteroids Clone")
18
+ clock = pygame.time.Clock()
19
+
20
+ # --- Utility Functions ---
21
+
22
+ def wrap_around(sprite):
23
+ """Wraps sprite position when it leaves the screen edges."""
24
+ if sprite.rect.left > WIDTH:
25
+ sprite.rect.right = 0
26
+ if sprite.rect.right < 0:
27
+ sprite.rect.left = WIDTH
28
+ if sprite.rect.top > HEIGHT:
29
+ sprite.rect.bottom = 0
30
+ if sprite.rect.bottom < 0:
31
+ sprite.rect.top = HEIGHT
32
+
33
+ def distance(p1, p2):
34
+ """Calculates the distance between two (x, y) points."""
35
+ return math.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
36
+
37
+ # --- Game Classes ---
38
+
39
+ class Ship(pygame.sprite.Sprite):
40
+ def __init__(self):
41
+ super().__init__()
42
+ # The ship is a simple white triangle pointing up
43
+ self.image_orig = pygame.Surface((30, 30), pygame.SRCALPHA)
44
+ # Draw the triangle: (15, 0) is the nose
45
+ pygame.draw.polygon(self.image_orig, WHITE, [(15, 0), (0, 30), (30, 30)])
46
+ self.image = self.image_orig.copy()
47
+ self.rect = self.image.get_rect(center=(WIDTH / 2, HEIGHT / 2))
48
+
49
+ self.pos = pygame.math.Vector2(self.rect.center)
50
+ self.vel = pygame.math.Vector2(0, 0)
51
+ self.angle = 0 # 0 degrees is facing up
52
+ self.rotation_speed = 4
53
+
54
+ def update(self):
55
+ # 1. Handle Rotation
56
+ keys = pygame.key.get_pressed()
57
+ if keys[pygame.K_LEFT]:
58
+ self.angle += self.rotation_speed
59
+ if keys[pygame.K_RIGHT]:
60
+ self.angle -= self.rotation_speed
61
+
62
+ # Keep angle within [0, 360)
63
+ self.angle %= 360
64
+
65
+ # Rotate the ship image
66
+ self.image = pygame.transform.rotate(self.image_orig, self.angle)
67
+ self.rect = self.image.get_rect(center=self.rect.center)
68
+
69
+ # 2. Handle Thrust
70
+ if keys[pygame.K_UP]:
71
+ # Convert angle (degrees) to radians for trig functions
72
+ angle_rad = math.radians(self.angle - 90) # Adjust angle for standard math orientation (0=right, 90=up)
73
+
74
+ # Calculate thrust vector
75
+ thrust_x = SHIP_THRUST * math.cos(angle_rad)
76
+ thrust_y = SHIP_THRUST * math.sin(angle_rad)
77
+
78
+ self.vel += pygame.math.Vector2(thrust_x, thrust_y)
79
+
80
+ # 3. Apply Friction/Limit Speed (optional: implement max speed)
81
+ if self.vel.length() > SHIP_MAX_SPEED:
82
+ self.vel.scale_to_length(SHIP_MAX_SPEED)
83
+
84
+ # 4. Apply Movement
85
+ self.pos += self.vel
86
+ self.rect.center = (int(self.pos.x), int(self.pos.y))
87
+
88
+ # 5. Wrap Around Screen
89
+ wrap_around(self)
90
+
91
+ def shoot(self, all_sprites, bullets):
92
+ # Calculate bullet starting position and velocity
93
+ angle_rad = math.radians(self.angle - 90)
94
+
95
+ # The nose of the ship is the spawn point. This is an approximation.
96
+ spawn_x = self.pos.x + 15 * math.cos(angle_rad)
97
+ spawn_y = self.pos.y + 15 * math.sin(angle_rad)
98
+
99
+ bullet_vel_x = BULLET_SPEED * math.cos(angle_rad)
100
+ bullet_vel_y = BULLET_SPEED * math.sin(angle_rad)
101
+
102
+ bullet = Bullet(spawn_x, spawn_y, bullet_vel_x, bullet_vel_y)
103
+ all_sprites.add(bullet)
104
+ bullets.add(bullet)
105
+
106
+
107
+ class Asteroid(pygame.sprite.Sprite):
108
+ def __init__(self, size, center):
109
+ super().__init__()
110
+ self.size = size # 3 (large), 2 (medium), 1 (small)
111
+ self.radius = self.size * 15
112
+
113
+ self.image = pygame.Surface((self.radius * 2, self.radius * 2), pygame.SRCALPHA)
114
+ # Draw a white circle for simplicity; complex asteroids use polygon shapes
115
+ pygame.draw.circle(self.image, WHITE, (self.radius, self.radius), self.radius, 1)
116
+
117
+ self.rect = self.image.get_rect(center=center)
118
+
119
+ # Random initial velocity
120
+ angle = random.uniform(0, 2 * math.pi)
121
+ speed = random.uniform(0.5, 2)
122
+ self.vel = pygame.math.Vector2(speed * math.cos(angle), speed * math.sin(angle))
123
+
124
+ def update(self):
125
+ self.rect.centerx += self.vel.x
126
+ self.rect.centery += self.vel.y
127
+ wrap_around(self)
128
+
129
+ def spawn_smaller(self):
130
+ """Splits the asteroid into two smaller ones."""
131
+ if self.size > 1:
132
+ new_size = self.size - 1
133
+ # Spawn two smaller asteroids moving in opposite directions
134
+ return [
135
+ Asteroid(new_size, self.rect.center),
136
+ Asteroid(new_size, self.rect.center)
137
+ ]
138
+ return []
139
+
140
+ class Bullet(pygame.sprite.Sprite):
141
+ def __init__(self, x, y, vel_x, vel_y):
142
+ super().__init__()
143
+ self.image = pygame.Surface((4, 4))
144
+ self.image.fill(WHITE)
145
+ self.rect = self.image.get_rect(center=(x, y))
146
+ self.vel = pygame.math.Vector2(vel_x, vel_y)
147
+ # Bullets have a limited lifespan
148
+ self.lifespan = 90 # 90 frames (1.5 seconds at 60 FPS)
149
+
150
+ def update(self):
151
+ self.rect.centerx += self.vel.x
152
+ self.rect.centery += self.vel.y
153
+
154
+ # Check if bullet goes off screen (optional: wrap, but standard is destroy)
155
+ if not (0 < self.rect.x < WIDTH and 0 < self.rect.y < HEIGHT):
156
+ self.kill()
157
+
158
+ # Decrease lifespan
159
+ self.lifespan -= 1
160
+ if self.lifespan <= 0:
161
+ self.kill()
162
+
163
+ # --- Setup Game State ---
164
+ all_sprites = pygame.sprite.Group()
165
+ asteroids = pygame.sprite.Group()
166
+ bullets = pygame.sprite.Group()
167
+
168
+ player = Ship()
169
+ all_sprites.add(player)
170
+
171
+ # Spawn initial asteroids
172
+ for i in range(4):
173
+ # Spawn large asteroids away from the center
174
+ spawn_pos = (random.choice([random.randrange(0, 100), random.randrange(WIDTH - 100, WIDTH)]),
175
+ random.choice([random.randrange(0, 100), random.randrange(HEIGHT - 100, HEIGHT)]))
176
+ asteroid = Asteroid(3, spawn_pos)
177
+ all_sprites.add(asteroid)
178
+ asteroids.add(asteroid)
179
+
180
+ # --- Game Loop ---
181
+ running = True
182
+ last_shot_time = 0
183
+ SHOT_DELAY = 250 # milliseconds
184
+
185
+ while running:
186
+ # --- 1. Process Input/Events ---
187
+ for event in pygame.event.get():
188
+ if event.type == pygame.QUIT:
189
+ running = False
190
+ if event.type == pygame.KEYDOWN:
191
+ if event.key == pygame.K_SPACE:
192
+ # Add shot delay to prevent rapid-fire on key-hold
193
+ now = pygame.time.get_ticks()
194
+ if now - last_shot_time > SHOT_DELAY:
195
+ player.shoot(all_sprites, bullets)
196
+ last_shot_time = now
197
+
198
+ # --- 2. Update Game State ---
199
+ all_sprites.update()
200
+
201
+ # --- 3. Check Collisions ---
202
+
203
+ # Bullet hits Asteroid
204
+ hits = pygame.sprite.groupcollide(bullets, asteroids, True, False)
205
+ for bullet, hit_asteroids in hits.items():
206
+ for asteroid in hit_asteroids:
207
+ # Create smaller asteroids
208
+ new_asteroids = asteroid.spawn_smaller()
209
+ for new_ast in new_asteroids:
210
+ all_sprites.add(new_ast)
211
+ asteroids.add(new_ast)
212
+
213
+ asteroid.kill() # Destroy the original asteroid
214
+
215
+ # Ship hits Asteroid
216
+ # 'pygame.sprite.collide_mask' is better for irregular shapes but 'pygame.sprite.collide_circle' is fine here
217
+ if pygame.sprite.spritecollide(player, asteroids, True, pygame.sprite.collide_mask):
218
+ # Game Over logic (reset or show message)
219
+ print("Ship Destroyed! Game Over.")
220
+ running = False # End the game for simplicity
221
+
222
+ # --- 4. Render/Draw ---
223
+ screen.fill(BLACK) # Clear the screen
224
+ all_sprites.draw(screen) # Draw all sprites
225
+
226
+ # Update the full display surface
227
+ pygame.display.flip()
228
+
229
+ # Wait for the next frame
230
+ clock.tick(FPS)
231
+
232
+ pygame.quit()