From 275022f2950867e57fdbbc53371d4951fc29172f Mon Sep 17 00:00:00 2001 From: Itsig0 Date: Mon, 10 Mar 2025 13:00:07 +0100 Subject: [PATCH] the game is gamin --- asteroid.py | 31 ++++++++++++++++++++++++++ asteroidfield.py | 52 +++++++++++++++++++++++++++++++++++++++++++ circleshape.py | 25 +++++++++++++++++++++ constants.py | 8 +++++++ main.py | 39 +++++++++++++++++++++++++++++++++ player.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ shot.py | 14 ++++++++++++ 7 files changed, 226 insertions(+) create mode 100644 asteroid.py create mode 100644 asteroidfield.py create mode 100644 circleshape.py create mode 100644 player.py create mode 100644 shot.py diff --git a/asteroid.py b/asteroid.py new file mode 100644 index 0000000..3fbe1b7 --- /dev/null +++ b/asteroid.py @@ -0,0 +1,31 @@ +import random +import pygame +from circleshape import CircleShape +from constants import ASTEROID_MIN_RADIUS + +class Asteroid(CircleShape): + def __init__(self, x, y, radius): + super().__init__(x, y, radius) + + def draw(self, screen): + pygame.draw.circle(screen, "white", self.position, self.radius, 2) + + def update(self, dt): + self.position += self.velocity * dt + + def split(self): + self.kill() + if self.radius <= ASTEROID_MIN_RADIUS: + return + + new_angle = random.uniform(20,50) + new_velocity_1 = self.velocity.rotate(new_angle) + new_velocity_2 = self.velocity.rotate(-new_angle) + + new_radius = self.radius - ASTEROID_MIN_RADIUS + + new_asteroid_1 = Asteroid(self.position[0], self.position[1], new_radius) + new_asteroid_2 = Asteroid(self.position[0], self.position[1], new_radius) + + new_asteroid_1.velocity = new_velocity_1 * 1.2 + new_asteroid_2.velocity = new_velocity_2 * 1.2 diff --git a/asteroidfield.py b/asteroidfield.py new file mode 100644 index 0000000..86b5c63 --- /dev/null +++ b/asteroidfield.py @@ -0,0 +1,52 @@ +import pygame +import random +from asteroid import Asteroid +from constants import * + + +class AsteroidField(pygame.sprite.Sprite): + edges = [ + [ + pygame.Vector2(1, 0), + lambda y: pygame.Vector2(-ASTEROID_MAX_RADIUS, y * SCREEN_HEIGHT), + ], + [ + pygame.Vector2(-1, 0), + lambda y: pygame.Vector2( + SCREEN_WIDTH + ASTEROID_MAX_RADIUS, y * SCREEN_HEIGHT + ), + ], + [ + pygame.Vector2(0, 1), + lambda x: pygame.Vector2(x * SCREEN_WIDTH, -ASTEROID_MAX_RADIUS), + ], + [ + pygame.Vector2(0, -1), + lambda x: pygame.Vector2( + x * SCREEN_WIDTH, SCREEN_HEIGHT + ASTEROID_MAX_RADIUS + ), + ], + ] + + def __init__(self): + pygame.sprite.Sprite.__init__(self, self.containers) + self.spawn_timer = 0.0 + + def spawn(self, radius, position, velocity): + asteroid = Asteroid(position.x, position.y, radius) + asteroid.velocity = velocity + + def update(self, dt): + self.spawn_timer += dt + if self.spawn_timer > ASTEROID_SPAWN_RATE: + self.spawn_timer = 0 + + # spawn a new asteroid at a random edge + edge = random.choice(self.edges) + speed = random.randint(40, 100) + velocity = edge[0] * speed + velocity = velocity.rotate(random.randint(-30, 30)) + position = edge[1](random.uniform(0, 1)) + kind = random.randint(1, ASTEROID_KINDS) + self.spawn(ASTEROID_MIN_RADIUS * kind, position, velocity) + diff --git a/circleshape.py b/circleshape.py new file mode 100644 index 0000000..d6a834d --- /dev/null +++ b/circleshape.py @@ -0,0 +1,25 @@ +import pygame + +# Base class for game objects +class CircleShape(pygame.sprite.Sprite): + def __init__(self, x, y, radius): + # we will be using this later + if hasattr(self, "containers"): + super().__init__(self.containers) + else: + super().__init__() + + self.position = pygame.Vector2(x, y) + self.velocity = pygame.Vector2(0, 0) + self.radius = radius + + def draw(self, screen): + # sub-classes must override + pass + + def update(self, dt): + # sub-classes must override + pass + + def colission(self, circle): + return self.position.distance_to(circle.position) <= (self.radius + circle.radius) diff --git a/constants.py b/constants.py index 48312a2..6032da8 100644 --- a/constants.py +++ b/constants.py @@ -1,6 +1,14 @@ SCREEN_WIDTH = 1280 SCREEN_HEIGHT = 720 +PLAYER_RADIUS = 20 +PLAYER_SPEED = 20 +PLAYER_TURN_SPEED = 300 +PLAYER_SHOT_SPEED = 500 +PLAYER_SHOOT_COOLDOWN = 0.3 + +SHOT_RADIUS = 5 + ASTEROID_MIN_RADIUS = 20 ASTEROID_KINDS = 3 ASTEROID_SPAWN_RATE = 0.8 # seconds diff --git a/main.py b/main.py index 51c48a5..fd89f1e 100644 --- a/main.py +++ b/main.py @@ -1,8 +1,13 @@ # this allows us to use code from # the open-source pygame library # throughout this file +import sys import pygame from constants import * +from player import * +from asteroid import * +from asteroidfield import * +from shot import * def main(): print("Starting Asteroids!") @@ -13,13 +18,47 @@ def main(): #set screen resolution screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) + clock = pygame.time.Clock() + dt = 0 + + #groups + updatable = pygame.sprite.Group() + drawable = pygame.sprite.Group() + asteroids = pygame.sprite.Group() + shots = pygame.sprite.Group() + + Player.containers = (updatable, drawable) + Asteroid.containers = (updatable, drawable, asteroids) + AsteroidField.containers = (updatable) + Shot.containers = (updatable, drawable, shots) + + player = Player(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2) + asteroidfield = AsteroidField() + while True: for event in pygame.event.get(): if event.type == pygame.QUIT: return + + updatable.update(dt) + + for a in asteroids: + if a.colission(player): + print("Game Over!") + sys.exit() + for s in shots: + if a.colission(s): + a.split() + s.kill() screen.fill((0,0,0)) + + for d in drawable: + d.draw(screen) + + pygame.display.flip() + dt = clock.tick(60) / 1000 if __name__ == "__main__": main() diff --git a/player.py b/player.py new file mode 100644 index 0000000..1d794fb --- /dev/null +++ b/player.py @@ -0,0 +1,57 @@ +import pygame +from constants import PLAYER_RADIUS, PLAYER_TURN_SPEED, PLAYER_SPEED, PLAYER_SHOT_SPEED, PLAYER_SHOOT_COOLDOWN +from circleshape import CircleShape +from shot import Shot + +class Player(CircleShape): + def __init__(self, x, y): + super().__init__(x, y, PLAYER_RADIUS) + self.rotation = 0 + self.shot_cooldown_counter = 0 + + def draw(self, screen): + pygame.draw.polygon(screen, "white", self.triangle(), 2) + + def update(self, dt): + self.shot_cooldown_counter -= dt + + keys = pygame.key.get_pressed() + + if keys[pygame.K_a]: + self.rotate(-dt) + + if keys[pygame.K_d]: + self.rotate(dt) + + if keys[pygame.K_w]: + self.move(dt) + + if keys[pygame.K_s]: + self.move(-dt) + + if keys[pygame.K_SPACE]: + self.shoot() + + + def rotate(self, dt): + self.rotation += PLAYER_TURN_SPEED * dt + + def move(self, dt): + forward = pygame.Vector2(0, 1).rotate(self.rotation) + self.position += forward * PLAYER_SPEED * dt + + def shoot(self): + if self.shot_cooldown_counter > 0: + return + shot = Shot(self.position[0], self.position[1]) + shot.velocity = pygame.Vector2(0, 1).rotate(self.rotation) * PLAYER_SHOT_SPEED + self.shot_cooldown_counter = PLAYER_SHOOT_COOLDOWN + + + def triangle(self): + forward = pygame.Vector2(0, 1).rotate(self.rotation) + right = pygame.Vector2(0, 1).rotate(self.rotation + 90) * self.radius / 1.5 + a = self.position + forward * self.radius + b = self.position - forward * self.radius - right + c = self.position - forward * self.radius + right + return [a, b, c] diff --git a/shot.py b/shot.py new file mode 100644 index 0000000..6131172 --- /dev/null +++ b/shot.py @@ -0,0 +1,14 @@ +import pygame +from circleshape import CircleShape +from constants import SHOT_RADIUS + +class Shot(CircleShape): + def __init__(self, x, y): + super().__init__(x, y, SHOT_RADIUS) + self.rotation = 0 + + def draw(self, screen): + pygame.draw.circle(screen, "white", self.position, self.radius, 2) + + def update(self, dt): + self.position += self.velocity * dt