Created
July 10, 2025 06:20
-
-
Save moriarty99779/945b9c9f158726582f5c0991a132a60c to your computer and use it in GitHub Desktop.
Simple Space Invaders using Python and PyGame
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pygame | |
import random | |
import math | |
import os | |
# Initialize | |
pygame.init() | |
WIDTH, HEIGHT = 800, 600 | |
screen = pygame.display.set_mode((WIDTH, HEIGHT)) | |
pygame.display.set_caption("Space Invaders") | |
# Assets folder | |
ASSETS = "assets" | |
# Load images | |
player_img = pygame.image.load(os.path.join(ASSETS, "player.png")) | |
enemy_img = pygame.image.load(os.path.join(ASSETS, "enemy.png")) | |
bullet_img = pygame.image.load(os.path.join(ASSETS, "bullet.png")) | |
enemy_bullet_img = pygame.image.load(os.path.join(ASSETS, "enemy_bullet.png")) | |
explosion_imgs = [pygame.image.load(os.path.join(ASSETS, f"explosion{i}.png")) for i in range(1, 5)] | |
# Load sounds | |
shoot_sound = pygame.mixer.Sound(os.path.join(ASSETS, "shoot.wav")) | |
explosion_sound = pygame.mixer.Sound(os.path.join(ASSETS, "explosion.wav")) | |
game_over_sound = pygame.mixer.Sound(os.path.join(ASSETS, "gameover.wav")) | |
# Colors | |
WHITE = (255, 255, 255) | |
background_color = (10, 10, 30) | |
font = pygame.font.SysFont("consolas", 28) | |
# Game Variables | |
player_x = 370 | |
player_y = 500 | |
player_speed = 5 | |
bullet_x = 0 | |
bullet_y = player_y | |
bullet_speed = 7 | |
bullet_state = "ready" | |
score = 0 | |
level = 1 | |
lives = 3 | |
enemy_rows = 3 | |
enemy_cols = 6 | |
enemy_bullets = [] | |
enemy_bullet_speed = 4 | |
enemy_fire_delay = 50 # Lower = more frequent | |
# Enemies | |
def spawn_enemies(rows): | |
enemies = [] | |
for row in range(rows): | |
for col in range(enemy_cols): | |
x = 100 + col * 80 | |
y = 50 + row * 60 | |
enemy = {"x": x, "y": y, "x_change": 2, "y_change": 40, "alive": True} | |
enemies.append(enemy) | |
return enemies | |
enemies = spawn_enemies(enemy_rows) | |
# Explosion animation | |
explosions = [] | |
def show_explosion(x, y): | |
explosions.append({"x": x, "y": y, "frame": 0}) | |
def draw_explosions(): | |
for e in explosions[:]: | |
screen.blit(explosion_imgs[e["frame"] // 5], (e["x"], e["y"])) | |
e["frame"] += 1 | |
if e["frame"] >= 20: | |
explosions.remove(e) | |
# Helper Functions | |
def is_collision(x1, y1, x2, y2, threshold=27): | |
return math.hypot(x2 - x1, y2 - y1) < threshold | |
def fire_bullet(): | |
global bullet_state, bullet_x, bullet_y | |
bullet_state = "fire" | |
bullet_x = player_x | |
bullet_y = player_y | |
shoot_sound.play() | |
def draw_enemies(): | |
for enemy in enemies: | |
if enemy["alive"]: | |
screen.blit(enemy_img, (enemy["x"], enemy["y"])) | |
def fire_enemy_bullet(enemy): | |
if random.randint(0, enemy_fire_delay) == 0: | |
bullet = {"x": enemy["x"] + 24, "y": enemy["y"] + 32} | |
enemy_bullets.append(bullet) | |
def game_over(): | |
game_over_sound.play() | |
msg = font.render("GAME OVER", True, WHITE) | |
screen.blit(msg, (WIDTH//2 - 100, HEIGHT//2 - 20)) | |
pygame.display.update() | |
pygame.time.wait(3000) | |
pygame.quit() | |
exit() | |
# Main loop | |
clock = pygame.time.Clock() | |
running = True | |
while running: | |
screen.fill(background_color) | |
clock.tick(60) | |
for event in pygame.event.get(): | |
if event.type == pygame.QUIT: | |
running = False | |
# Keypress | |
keys = pygame.key.get_pressed() | |
if keys[pygame.K_LEFT]: | |
player_x -= player_speed | |
if keys[pygame.K_RIGHT]: | |
player_x += player_speed | |
if keys[pygame.K_SPACE] and bullet_state == "ready": | |
fire_bullet() | |
# Player bounds | |
player_x = max(0, min(player_x, WIDTH - 64)) | |
# Bullet movement | |
if bullet_state == "fire": | |
screen.blit(bullet_img, (bullet_x + 16, bullet_y)) | |
bullet_y -= bullet_speed | |
if bullet_y <= 0: | |
bullet_state = "ready" | |
# Enemy logic | |
enemy_direction_change = False | |
for enemy in enemies: | |
if enemy["alive"]: | |
enemy["x"] += enemy["x_change"] | |
if enemy["x"] <= 0 or enemy["x"] >= WIDTH - 64: | |
enemy["x_change"] *= -1 | |
enemy_direction_change = True | |
fire_enemy_bullet(enemy) | |
# Collision with bullet | |
if bullet_state == "fire" and is_collision(bullet_x, bullet_y, enemy["x"], enemy["y"]): | |
enemy["alive"] = False | |
show_explosion(enemy["x"], enemy["y"]) | |
explosion_sound.play() | |
score += 10 | |
bullet_state = "ready" | |
# Check game over condition | |
if enemy["y"] > player_y - 40: | |
game_over() | |
if enemy_direction_change: | |
for enemy in enemies: | |
enemy["y"] += enemy["y_change"] | |
# Enemy bullets | |
for eb in enemy_bullets[:]: | |
screen.blit(enemy_bullet_img, (eb["x"], eb["y"])) | |
eb["y"] += enemy_bullet_speed | |
if eb["y"] > HEIGHT: | |
enemy_bullets.remove(eb) | |
elif is_collision(eb["x"], eb["y"], player_x, player_y, 30): | |
show_explosion(player_x, player_y) | |
explosion_sound.play() | |
enemy_bullets.remove(eb) | |
lives -= 1 | |
if lives <= 0: | |
game_over() | |
# Level progression | |
if all(not e["alive"] for e in enemies): | |
level += 1 | |
enemy_rows += 1 | |
enemy_fire_delay = max(10, enemy_fire_delay - 5) | |
enemies = spawn_enemies(enemy_rows) | |
# Draw player and enemies | |
screen.blit(player_img, (player_x, player_y)) | |
draw_enemies() | |
draw_explosions() | |
# Score and lives | |
score_text = font.render(f"Score: {score} Lives: {lives} Level: {level}", True, WHITE) | |
screen.blit(score_text, (10, 10)) | |
pygame.display.update() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment