Skip to content

Commit 48a8fd5

Browse files
committed
Add : Space Invader Game
1 parent 62703d5 commit 48a8fd5

1 file changed

Lines changed: 348 additions & 0 deletions

File tree

Space invaders /space_invaders.py

Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
# space_invaders.py
2+
import pygame
3+
import random
4+
import math
5+
from collections import deque
6+
7+
# ---------- Config ----------
8+
WIDTH, HEIGHT = 900, 700
9+
FPS = 60
10+
PLAYER_SPEED = 420 # px/sec
11+
BULLET_SPEED = 700
12+
ENEMY_BASE_SPEED = 40 # px/sec
13+
WAVE_GAP = 1.2 # seconds between enemy spawn rows
14+
BOSS_EVERY = 5 # boss every N waves
15+
POWERUP_DURATION = 8.0 # seconds
16+
# ----------------------------
17+
18+
pygame.init()
19+
screen = pygame.display.set_mode((WIDTH, HEIGHT))
20+
pygame.display.set_caption("Space Invaders - Advanced (Pygame)")
21+
clock = pygame.time.Clock()
22+
font = pygame.font.SysFont("Arial", 20)
23+
big_font = pygame.font.SysFont("Arial", 40)
24+
25+
# Colors
26+
WHITE = (255, 255, 255)
27+
GREY = (180, 180, 180)
28+
BLACK = (0, 0, 0)
29+
RED = (220, 60, 60)
30+
GREEN = (60, 200, 80)
31+
YELLOW= (240, 220, 60)
32+
BLUE = (70, 130, 240)
33+
PURPLE= (170, 80, 200)
34+
35+
# ---------- Utility ----------
36+
def draw_text(surf, text, size, x, y, color=WHITE):
37+
f = pygame.font.SysFont("Arial", size)
38+
r = f.render(text, True, color)
39+
surf.blit(r, (x, y))
40+
41+
# ---------- Sprites ----------
42+
class Player(pygame.sprite.Sprite):
43+
def __init__(self):
44+
super().__init__()
45+
self.w, self.h = 54, 28
46+
self.surf = pygame.Surface((self.w, self.h), pygame.SRCALPHA)
47+
pygame.draw.polygon(self.surf, BLUE, [(0,self.h),(self.w/2,0),(self.w,self.h)])
48+
self.rect = self.surf.get_rect(midbottom=(WIDTH//2, HEIGHT - 24))
49+
self.speed = PLAYER_SPEED
50+
self.shoot_cooldown = 0.22
51+
self.shoot_timer = 0.0
52+
self.lives = 3
53+
self.invulnerable = 0.0
54+
self.shield = False
55+
self.powerups = {} # name: expiry_time
56+
self.score = 0
57+
58+
def update(self, dt, keys):
59+
move = 0
60+
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
61+
move -= 1
62+
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
63+
move += 1
64+
self.rect.x += move * self.speed * dt
65+
self.rect.x = max(6, min(WIDTH - self.rect.width - 6, self.rect.x))
66+
67+
self.shoot_timer = max(0, self.shoot_timer - dt)
68+
if self.invulnerable > 0:
69+
self.invulnerable -= dt
70+
71+
now = pygame.time.get_ticks() / 1000.0
72+
expired = [p for p,e in self.powerups.items() if e <= now]
73+
for p in expired:
74+
del self.powerups[p]
75+
self.shield = 'shield' in self.powerups
76+
77+
def can_shoot(self):
78+
return self.shoot_timer <= 0
79+
80+
def shoot(self, bullets_group):
81+
now = pygame.time.get_ticks() / 1000.0
82+
rapid = 'rapid' in self.powerups
83+
spread = 'spread' in self.powerups
84+
85+
if not self.can_shoot():
86+
return
87+
self.shoot_timer = 0.08 if rapid else self.shoot_cooldown
88+
89+
if spread:
90+
angles = [-12, 0, 12]
91+
for a in angles:
92+
bullets_group.add(Bullet(self.rect.centerx, self.rect.top-8, a, is_player=True))
93+
else:
94+
bullets_group.add(Bullet(self.rect.centerx, self.rect.top-8, 0, is_player=True))
95+
96+
def hit(self):
97+
if self.invulnerable > 0:
98+
return False
99+
if self.shield:
100+
if 'shield' in self.powerups:
101+
del self.powerups['shield']
102+
self.shield = False
103+
self.invulnerable = 0.5
104+
return False
105+
self.lives -= 1
106+
self.invulnerable = 1.0
107+
return True
108+
109+
110+
class Bullet(pygame.sprite.Sprite):
111+
def __init__(self, x, y, angle_deg, is_player=True):
112+
super().__init__()
113+
self.is_player = is_player
114+
self.surf = pygame.Surface((4, 12), pygame.SRCALPHA)
115+
color = YELLOW if is_player else RED
116+
pygame.draw.rect(self.surf, color, (0,0,4,12))
117+
self.rect = self.surf.get_rect(center=(x,y))
118+
self.angle = math.radians(angle_deg)
119+
direction = -1 if is_player else 1
120+
self.vx = math.sin(self.angle) * BULLET_SPEED * (1 if is_player else 0.7)
121+
self.vy = math.cos(self.angle) * BULLET_SPEED * direction
122+
123+
def update(self, dt):
124+
self.rect.x += int(self.vx * dt)
125+
self.rect.y += int(self.vy * dt)
126+
if self.rect.bottom < -10 or self.rect.top > HEIGHT + 10 or self.rect.right < -40 or self.rect.left > WIDTH + 40:
127+
self.kill()
128+
129+
130+
class Enemy(pygame.sprite.Sprite):
131+
COLORS = [(200,80,80),(80,200,120),(200,180,60)]
132+
def __init__(self, x, y, row, speed=ENEMY_BASE_SPEED):
133+
super().__init__()
134+
self.size = 36
135+
self.surf = pygame.Surface((self.size, self.size), pygame.SRCALPHA)
136+
pygame.draw.rect(self.surf, Enemy.COLORS[row % len(Enemy.COLORS)], (0,0,self.size,self.size), border_radius=6)
137+
pygame.draw.rect(self.surf, BLACK, (6,8, self.size-12,8))
138+
self.rect = self.surf.get_rect(topleft=(x,y))
139+
self.row = row
140+
self.speed = speed
141+
self.direction = 1
142+
self.hp = 1
143+
144+
def update(self, dt, sway):
145+
dx, dy = sway
146+
self.rect.x += int(dx * dt)
147+
self.rect.y += int(dy * dt)
148+
149+
150+
class Boss(pygame.sprite.Sprite):
151+
def __init__(self, x, y, hp):
152+
super().__init__()
153+
self.w = 220
154+
self.h = 90
155+
self.surf = pygame.Surface((self.w, self.h), pygame.SRCALPHA)
156+
self.rect = self.surf.get_rect(center=(x,y))
157+
self.max_hp = hp
158+
self.hp = hp
159+
self.phase = 1
160+
self.attack_timer = 0.0
161+
self.vx = 80
162+
self.dir = 1
163+
164+
def update(self, dt):
165+
self.rect.x += int(self.dir * self.vx * dt)
166+
if self.rect.left < 30:
167+
self.rect.left = 30
168+
self.dir *= -1
169+
if self.rect.right > WIDTH - 30:
170+
self.rect.right = WIDTH - 30
171+
self.dir *= -1
172+
ratio = self.hp / max(1, self.max_hp)
173+
self.phase = 1 if ratio > 0.66 else (2 if ratio > 0.33 else 3)
174+
175+
def draw(self, surface):
176+
surf = pygame.Surface((self.w, self.h), pygame.SRCALPHA)
177+
pygame.draw.rect(surf, PURPLE, (0, 0, self.w, self.h), border_radius=14)
178+
for i in range(3):
179+
pygame.draw.circle(surf, (30,30,30), (40 + i*60, 34), 12)
180+
surface.blit(surf, self.rect.topleft)
181+
hp_w = int((self.hp / self.max_hp) * (self.w - 10))
182+
pygame.draw.rect(surface, (80,80,80), (self.rect.left+5, self.rect.top-12, self.w-10, 8))
183+
pygame.draw.rect(surface, (200,40,40), (self.rect.left+5, self.rect.top-12, hp_w, 8))
184+
185+
186+
class PowerUp(pygame.sprite.Sprite):
187+
TYPES = ['shield','rapid','spread']
188+
ICON = {'shield': GREEN, 'rapid': YELLOW, 'spread': PURPLE}
189+
def __init__(self, x, y, kind=None):
190+
super().__init__()
191+
self.kind = kind or random.choice(self.TYPES)
192+
self.surf = pygame.Surface((26,26), pygame.SRCALPHA)
193+
pygame.draw.circle(self.surf, PowerUp.ICON[self.kind], (13,13), 12)
194+
self.rect = self.surf.get_rect(center=(x,y))
195+
self.vy = 80
196+
197+
def update(self, dt):
198+
self.rect.y += int(self.vy * dt)
199+
if self.rect.top > HEIGHT + 10:
200+
self.kill()
201+
202+
203+
class Explosion(pygame.sprite.Sprite):
204+
def __init__(self, x, y, color=YELLOW):
205+
super().__init__()
206+
self.particles = []
207+
self.timer = 0.5
208+
self.x, self.y = x, y
209+
self.color = color
210+
for _ in range(12):
211+
angle = random.random()*math.pi*2
212+
speed = random.uniform(60,220)
213+
life = random.uniform(0.3,0.7)
214+
self.particles.append([x,y,math.cos(angle)*speed,math.sin(angle)*speed,life])
215+
216+
def update(self, dt):
217+
self.timer -= dt
218+
for p in self.particles:
219+
p[0] += p[2] * dt
220+
p[1] += p[3] * dt
221+
p[4] -= dt
222+
if self.timer <= 0:
223+
self.kill()
224+
225+
def draw(self, surf):
226+
for p in self.particles:
227+
if p[4] > 0:
228+
alpha = int(max(0, p[4])*255)
229+
s = pygame.Surface((6,6), pygame.SRCALPHA)
230+
s.fill((*self.color, alpha))
231+
surf.blit(s, (p[0]-3,p[1]-3))
232+
233+
234+
# ---------- Wave Manager ----------
235+
class WaveManager:
236+
def __init__(self):
237+
self.wave = 0
238+
self.enemies_group = pygame.sprite.Group()
239+
self.enemy_spawn_queue = deque()
240+
self.sway_dir = 1
241+
self.sway_dy = 10
242+
self.enemy_speed = ENEMY_BASE_SPEED
243+
244+
def start_next_wave(self):
245+
self.wave += 1
246+
self.enemies_group.empty()
247+
self.enemy_spawn_queue.clear()
248+
self.enemy_speed = ENEMY_BASE_SPEED + (self.wave - 1) * 6
249+
if self.wave % BOSS_EVERY == 0:
250+
return 'boss'
251+
rows = min(5, 2 + self.wave // 2)
252+
cols = min(12, 6 + self.wave)
253+
margin_x, margin_y = 60, 80
254+
spacing_x = (WIDTH - 2*margin_x) // cols
255+
for r in range(rows):
256+
y = margin_y + r * 56
257+
for c in range(cols):
258+
x = margin_x + c * spacing_x + random.randint(-6,6)
259+
e = Enemy(x, y, r, speed=self.enemy_speed)
260+
e.hp = 1 + (r // 2)
261+
self.enemies_group.add(e)
262+
return 'normal'
263+
264+
def update(self, dt):
265+
if self.enemies_group:
266+
left = min(e.rect.left for e in self.enemies_group)
267+
right = max(e.rect.right for e in self.enemies_group)
268+
move_dx = self.enemy_speed * self.sway_dir * 0.6
269+
move_dy = 0
270+
if left < 20 and self.sway_dir < 0:
271+
self.sway_dir *= -1
272+
move_dx = 0
273+
move_dy = self.sway_dy * 4
274+
if right > WIDTH-20 and self.sway_dir > 0:
275+
self.sway_dir *= -1
276+
move_dx = 0
277+
move_dy = self.sway_dy * 4
278+
for e in list(self.enemies_group):
279+
e.update(dt, (move_dx, move_dy))
280+
281+
282+
# ---------- Game ----------
283+
class Game:
284+
def __init__(self):
285+
self.player = Player()
286+
self.player_group = pygame.sprite.GroupSingle(self.player)
287+
self.bullets = pygame.sprite.Group()
288+
self.enemy_bullets = pygame.sprite.Group()
289+
self.wave_manager = WaveManager()
290+
self.boss = None
291+
self.boss_group = pygame.sprite.Group()
292+
self.powerups = pygame.sprite.Group()
293+
self.explosions = pygame.sprite.Group()
294+
self.running = True
295+
self.paused = False
296+
self.state = 'playing'
297+
self.wave_manager.start_next_wave()
298+
299+
def update(self, dt, keys):
300+
if self.paused or not self.running:
301+
return
302+
303+
self.player.update(dt, keys)
304+
if keys[pygame.K_SPACE] or keys[pygame.K_w] or keys[pygame.K_UP]:
305+
self.player.shoot(self.bullets)
306+
307+
self.bullets.update(dt)
308+
self.enemy_bullets.update(dt)
309+
self.powerups.update(dt)
310+
self.explosions.update(dt)
311+
312+
if self.state == 'playing':
313+
self.wave_manager.update(dt)
314+
315+
for e in list(self.wave_manager.enemies_group):
316+
shoot_chance = 0.0009 + self.wave_manager.wave * 0.0007 + (e.row * 0.0004)
317+
if random.random() < shoot_chance * (60 * dt):
318+
self.enemy_bullets.add(Bullet(e.rect.centerx, e.rect.bottom + 6, 0, is_player=False))
319+
320+
321+
# ---------- Main Loop ----------
322+
def main():
323+
game = Game()
324+
running = True
325+
while running:
326+
dt = clock.tick(FPS) / 1000.0
327+
keys = pygame.key.get_pressed()
328+
329+
for event in pygame.event.get():
330+
if event.type == pygame.QUIT:
331+
running = False
332+
333+
screen.fill(BLACK)
334+
game.update(dt, keys)
335+
336+
game.player_group.draw(screen)
337+
game.wave_manager.enemies_group.draw(screen)
338+
game.bullets.draw(screen)
339+
game.enemy_bullets.draw(screen)
340+
game.powerups.draw(screen)
341+
game.explosions.draw(screen)
342+
343+
pygame.display.flip()
344+
345+
pygame.quit()
346+
347+
if __name__ == "__main__":
348+
main()

0 commit comments

Comments
 (0)