User Tools

Site Tools


bullet_game

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
bullet_game [2024/08/31 01:47] appledogbullet_game [2024/09/11 06:43] (current) – created appledog
Line 6: Line 6:
 * Refactoring * Refactoring
 ** Encapsulation ** Encapsulation
 +** Light Commentary (code commentary) ex. IN and OUT for functions.
  
 The primary goal is encapsulation. So, we're going to break up the old main into several new classes. The primary goal is encapsulation. So, we're going to break up the old main into several new classes.
  
-== Screen and Window +== PyGame Framework 
-The first thing we always do is have a Screen class and a Window classThe Screen class defines things like WIDTH and HEIGHT, and the Window class holds the pygame screen and other pygame initialization.+The first part of this is the use of [[pygame framework]]We'll describe the additions here.
  
-Initially, this code would appear at the start of a main.py. Later we moved it into class Game's __init__. However, to keep things clean we made it into it's own class.+=== Also See 
 +* [[Box Sprites]] -- the kind of sprites first used in this game.
  
-=== class Screen:+== Bullet Game 
 +Here is the code we added, starting with main.py: 
 + 
 +=== main.py
 <Code:Python> <Code:Python>
 +from Window import *
 +from Game import *
 +
 +def main():
 +    window = Window()
 +    window.setCaption("Bullet Game")
 +    window.setSize(Screen.WIDTH, Screen.HEIGHT)
 +    window.setFont("assets/ps2p.ttf", 32)
 +
 +    game = Game(window)
 +    game.start()
 +
 +if __name__ == "__main__":
 +    main()
 </Code> </Code>
  
-=== class Window: +=== Screen,py 
-==== def __init__ +<Code:Python>
-This calls pygame.init(). See [[main]]+
  
 +class Screen:
 +    WIDTH = 800
 +    HEIGHT = 600
 +    FPS = 60
  
-==== full code+    MARGIN 96 
 +    GAMETOP MARGIN 
 +    GAMEBOT HEIGHT - MARGIN 
 +</Code> 
 + 
 + 
 +=== Game.py
 <Code:Python> <Code:Python>
 import pygame import pygame
 +import random
 +import pickle
  
-class Window: +from Screen import * 
-    def __init__(self): +from Window import * 
-        pygame.init()+from Bullet import * 
 +from Color import * 
 +from Screen import * 
 +from Zombie import * 
 +from SpaceShip import * 
 +from Sound import * 
 +from Player import * 
 +from Constants import * 
 +from Level import * 
 +from Star import *
  
-    def setLogo(self, filename): 
-        self.logo = pygame.image.load(filename) 
-        pygame.display.set_icon(self.logo) 
-        return self.logo 
  
-    def setCaption(self, cap): +class Game: 
-        pygame.display.set_caption(cap)+    def __init__(self, window): 
 +        self.window = window 
 +        self.sound = Sound()
  
-    def setSize(self, width, height):+        # Initialize Game World Data 
 +        # The Player & Level 
 +        self.player = Player() 
 +        self.player.hiscore = self.load_hiscore("score.txt"
 + 
 +        self.level = Level(1, self.player) 
 + 
 +        # Bullet list 
 +        self.bullets = pygame.sprite.Group() 
 + 
 +        # Zombies list 
 +        self.zombies = pygame.sprite.Group() 
 +        self.spaceships = pygame.sprite.Group() 
 + 
 +        # 30 random stars. 
 +        self.stars = [] 
 +        for n in range(30): 
 +            x = random.randint(0, Screen.WIDTH) 
 +            y = random.randint(0, Screen.HEIGHT) 
 +            self.stars.append((x, y)) 
 + 
 +        # All member variables are now globals: 
 +        # Try commenting it out to see what happens! 
 +        globals().update(self.__dict__) 
 + 
 +    ################################# 
 +    ##          Main Loop          ## 
 +    ################################# 
 + 
 +    def start(self): 
 + 
 +        # Clock for controlling the frame rate 
 +        clock = pygame.time.Clock() 
 + 
 +        # Main game loop 
 +        running = True 
 +        frame_counter = 0 
 + 
 +        ################## 
 +        # GAME LOOP ORDER OF EVENTS: 
 +        ################## 
 +        sound.play("start"
 +        # pygame.mixer.init() 
 +        # pygame.mixer.music.load('assets/sound/waveafterwave.mp3'
 +        # pygame.mixer.music.play() 
 + 
 +        # Start of Game Loop 
 +        time_start = time.time() * 1000 
 +        LEVEL_DELAY = 3000 
 +        while running: 
 +            time_clock = (time.time() * 1000) - time_start 
 +            frame_counter += 1 
 +            if frame_counter > 60: 
 +                frame_counter = 1 
 + 
 +            ######################## 
 +            # 1. EVENT HANDLING 
 +            ######################## 
 +            for event in pygame.event.get(): 
 +                if event.type == pygame.QUIT: 
 +                    running = False 
 +                    quit_game() 
 +                if event.type == pygame.KEYDOWN: 
 +                    if event.key == pygame.K_z: 
 +                        self.create_spaceship() 
 +                    if event.key == pygame.K_ESCAPE: 
 +                        self.quit_game() 
 +                    if event.key == pygame.K_q: 
 +                        self.quit_game() 
 + 
 +                    if event.key == pygame.K_w: 
 +                        x = self.player.rect.x + self.player.SIZE / 2 
 +                        y = self.player.rect.y + self.player.SIZE / 2 
 +                        self.create_bullet(x, y, 0, -1) 
 + 
 +            # Continuous (frame-by-frame) Key press handling 
 +            keys = pygame.key.get_pressed() 
 +            if keys[pygame.K_LEFT]: 
 +                self.player.rect.x -= self.player.SPEED 
 +            if keys[pygame.K_RIGHT]: 
 +                self.player.rect.x += self.player.SPEED 
 + 
 +            ####################### 
 +            # 2. RULES SECTION 
 +            ####################### 
 +            ### Level-specifc stuff: 
 +            if time_clock > LEVEL_DELAY: 
 +                commands = self.level.tick_hook() 
 + 
 +                for cmd in commands: 
 +                    if cmd == "create zombie": 
 +                        self.create_zombie() 
 + 
 +                    if cmd == "create spaceship": 
 +                        self.create_spaceship() 
 + 
 +                    if cmd == "go to level2": 
 +                        self.kill_enemies() 
 +                        time_start = time.time() * 1000 
 +                        self.level = Level(2, player) 
 + 
 +                    if cmd == "go to level3": 
 +                        self.kill_enemies() 
 +                        time_start = time.time() * 1000 
 +                        self.level = Level(3, self.player) 
 +                    if cmd == "go to level4": 
 +                        self.kill_enemies() 
 +                        time_start = time.time() * 1000 
 +                        self.level = Level(4, self.player) 
 + 
 +            # 2b. rules for bullets 
 +            # 2b.1. Update the bullets position. 
 +            self.bullets.update() 
 + 
 +            # Update all stars. 
 +            for i in range(len(self.stars)): 
 +                (x, y) = self.stars[i] 
 +                y = y + 1 
 + 
 +                if y > Screen.HEIGHT: 
 +                    x = random.randint(0, Screen.WIDTH) 
 +                    y = 0 
 + 
 +                self.stars[i] = (x, y) 
 + 
 +            if time_clock > LEVEL_DELAY: 
 +                # 2a rules for the square 
 +                # Keep the square within bounds 
 +                self.player.update() 
 + 
 +                # 2c. rules for zombies 
 +                # 2c.1. Update the zombies position. 
 +                self.zombies.update() 
 +                self.spaceships.update() 
 + 
 +            # Clear the screen 
 +            self.window.screen.fill(Color.BLACK) 
 + 
 +            # Draw stars first (because its a background) 
 +            for s in self.stars: 
 +                pygame.draw.rect(self.window.screen, "white", pygame.Rect(s[0], s[1], 2, 2)) 
 + 
 +            # Draw the square 
 +            # pygame.draw.rect(screen, square_color, (square_x, square_y, square_size, square_size)) 
 +            self.window.screen.blit(self.player.image, self.player.rect) 
 + 
 +            if time_clock > LEVEL_DELAY: 
 +                # Draw the zombies. 
 +                self.zombies.draw(self.window.screen) 
 +                self.spaceships.draw(self.window.screen) 
 + 
 +                # ships make a sound, if... 
 +                if self.spaceships: 
 +                    if frame_counter == 30: 
 +                        self.sound.play("pellet"
 + 
 +            # Draw the bullets. 
 +            self.bullets.draw(self.window.screen) 
 + 
 +            # write score. 
 +            if frame_counter < 30: 
 +                s = "1UP" 
 +                text_surface = self.window.font.render(s, True, "white"
 +                self.window.screen.blit(text_surface, (96, 10)) 
 + 
 +            s = f"{self.player.score:02}" 
 +            text_surface = self.window.font.render(s, True, "red"
 +            self.window.screen.blit(text_surface, (160, 42)) 
 + 
 +            # do high score. 
 +            s = "HIGH SCORE" 
 +            text_surface = self.window.font.render(s, True, "white"
 +            self.window.screen.blit(text_surface, (320, 10)) 
 + 
 +            s = f"{self.player.hiscore:02}" 
 +            text_surface = self.window.font.render(s, True, "red"
 +            self.window.screen.blit(text_surface, (512, 42)) 
 + 
 +            s = f"{self.level.level_number}" 
 +            text_surface = self.window.font.render(s, True, "red"
 +            self.window.screen.blit(text_surface, (700, 42)) 
 + 
 +            if time_clock < LEVEL_DELAY: 
 +                s = f"Level {self.level.level_number}" 
 +                text_surface = self.window.font.render(s, True, "white"
 +                tr = text_surface.get_rect() 
 +                tr.center = (Screen.WIDTH // 2, Screen.HEIGHT // 2 - 100) 
 +                self.window.screen.blit(text_surface, tr) 
 + 
 +            # Update the display 
 +            pygame.display.flip() 
 + 
 +            if time_clock > LEVEL_DELAY: 
 +                # 3. INTERSPRITE COLLISION 
 +                # 3a. bullets kill zombies 
 +                collisions = pygame.sprite.groupcollide(self.bullets, self.zombies, True, True) 
 +                collisions2 = pygame.sprite.groupcollide(self.bullets, self.spaceships, True, True) 
 + 
 +                if collisions: 
 +                    self.player.score = self.player.score + len(collisions) 
 +                    if self.player.score > self.player.hiscore: 
 +                        self.player.hiscore = self.player.score 
 + 
 +                if collisions or collisions2: 
 +                    n = random.randint(1, 2) 
 +                    if n == 1: 
 +                        self.sound.play("boom"
 +                    else: 
 +                        self.sound.play("boom7"
 + 
 +                if collisions2: 
 +                    self.player.score = self.player.score + (len(collisions2) * 10) 
 +                    if self.player.score > self.player.hiscore: 
 +                        self.player.hiscore = self.player.score 
 + 
 +                # Players may not touch zombies: 
 +                collided_enemies = pygame.sprite.spritecollide(self.player, self.zombies, False) 
 + 
 +                if collided_enemies: 
 +                    print("You are DEAD!"
 +                    print(" High score: " + str(self.player.hiscore)) 
 +                    print(" Your score: " + str(self.player.score)) 
 +                    self.quit_game() 
 + 
 +            # Control the frame rate 
 +            clock.tick(60) 
 + 
 +    ################################# 
 +    ##      Support Functions      ## 
 +    ################################# 
 + 
 +    def create_bullet(self, x, y, dx, dy): 
 +        self.bullet = Bullet(x, y, dx, dy) 
 +        self.bullets.add(self.bullet) 
 +        self.sound.play("laser"
 +        if Constants.VERBOSE: 
 +            print("bullet created"
 + 
 +    def create_zombie(self): 
 +        sx = random.randint(50, Screen.WIDTH - 50) 
 +        sy = 0 + Screen.MARGIN 
 +        dx = 0 
 +        dy = 1 
 +        z = Zombie(sx, sy, dx, dy) 
 +        self.zombies.add(z) 
 +        if Constants.VERBOSE: 
 +            print("zombie created"
 + 
 +    def create_spaceship(self): 
 +        sx = Screen.WIDTH 
 +        sy = 0 + Screen.MARGIN 
 +        dx = -1 
 +        dy = 0 
 + 
 +        if random.randint(1, 2) == 1: 
 +            dx = 1 
 +            sx = 0 
 + 
 +        s = SpaceShip(sx, sy, dx, dy) 
 +        self.spaceships.add(s) 
 +        if Constants.VERBOSE: 
 +            print("spaceship created"
 + 
 +    def kill_enemies(self): 
 +        for z in self.zombies: 
 +            z.kill() 
 +        for s in self.spaceships: 
 +            s.kill() 
 + 
 +    # Save the score to a text file. 
 +    def save_hiscore(self, filename, score): 
 +        with open(filename, 'w') as file: 
 +            file.write(str(score)) 
 + 
 +    # Load the score from a text file. 
 +    def load_hiscore(self, filename): 
 +        try: 
 +            with open(filename, 'r') as file: 
 +                score = int(file.read()) 
 +                return score 
 +        except: 
 +            print(f"Error. Resetting high score to 0.") 
 +            return 0 
 + 
 +    def quit_game(): 
 +        self.save_hiscore("score.txt", str(self.player.hiscore)) 
 +        pygame.quit() 
 +        quit() 
 +</Code> 
 + 
 +=== Level.py 
 +<Code:Python> 
 +import random 
 + 
 +class Level: 
 + 
 +    def __init__(self, n, p): 
 +        self.level_number = n 
 +        self.player = p 
 + 
 +        match self.level_number: 
 +            case 1: 
 +                self.tick_hook = self.level1 
 + 
 +            case 2: 
 +                self.tick_hook = self.level2 
 + 
 +            case 3: 
 +                self.tick_hook = self.level3 
 + 
 +            case 4: 
 +                self.tick_hook = self.level4 
 + 
 +            case default: 
 +                print("Unknown Level Number: " + str(n)) 
 +                print("Defaulting to Level 1!") 
 +                self.tick_hook = self.level1 
 + 
 +        return 
 + 
 + 
 +##### Tick Hooks 
 +    def level1(self): 
 +        commands = [] 
 + 
 +        if random.randint(1,45) == 1: 
 +            commands.append("create zombie"
 + 
 +        if self.player.score >= 20: 
 +            commands.append("go to level2"
 + 
 +        return commands 
 + 
 + 
 +    def level2(self): 
 +        commands = [] 
 + 
 +        if random.randint(1, 35) == 1: 
 +            commands.append("create zombie"
 + 
 +        if self.player.score >= 40: 
 +            commands.append("go to level3"
 + 
 +        return commands 
 + 
 +    def level3(self): 
 +        commands = [] 
 + 
 +        if random.randint(1, 30) == 1: 
 +            commands.append("create zombie"
 + 
 +        if random.randint(1, 500) == 1: 
 +            commands.append("create spaceship"
 + 
 +        if self.player.score >= 80: 
 +            commands.append("go to level4"
 + 
 +        return commands 
 + 
 +    def level4(self): 
 +        commands = [] 
 + 
 +        if random.randint(1, 20) == 1: 
 +            commands.append("create zombie"
 + 
 +        if random.randint(1, 350) == 1: 
 +            commands.append("create spaceship"
 + 
 +        #if self.player.score > 320: 
 +        #    commands.append("go to level5"
 + 
 +        return commands 
 +</Code> 
 + 
 +=== Object.py 
 +<Code:Python> 
 +import pygame 
 + 
 +class Object(pygame.sprite.Sprite): 
 +    def __init__(self, x, y, width, height, name=None): 
 +        super().__init__() 
 +        self.rect = pygame.Rect(x, y, width, height) 
 +        self.image = pygame.Surface((width, height), pygame.SRCALPHA)
         self.width = width         self.width = width
         self.height = height         self.height = height
-        self.size (width, height) +        self.name name
-        self.screen = pygame.display.set_mode(self.size) +
-        return self.screen+
  
-    def setFont(self, filenamesize): +    def draw(self, winoffset_x, offset_y): 
-        pygame.font.init(+        win.blit(self.image, (self.rect.x - offset_xself.rect.y + offset_y))
-        self.font = pygame.font.Font(filenamesize)+
  
-        font_width, font_height = self.font.size("@"+</Code>
-        self.fontwidth = font_width +
-        self.fontheight = size+
  
-        return self.font</Code>+=== Player.py 
 +<Code:Python> 
 +import pygame 
 +import time 
 +from Color import * 
 +from Screen import * 
 + 
 +# Define the Bullet Sprite class 
 +class Player(pygame.sprite.Sprite): 
 +    SPEED = 5 
 +    SIZE = 20 
 +    COLOR = Color.WHITE 
 + 
 +    def __init__(self): 
 +        super().__init__() 
 +        self.ticks = 0 
 +        self.loops = 0 
 + 
 +        # Create a surface for the bullet 
 +        self.image = pygame.Surface((self.SIZE, self.SIZE)) 
 + 
 +        # Fill the surface with a color 
 +        self.image.fill(self.COLOR) 
 + 
 +        # Get the rectangle for positioning 
 +        self.rect = self.image.get_rect() 
 + 
 +        # Set the initial position 
 +        self.rect.center = (Screen.WIDTH//2, Screen.HEIGHT-50) 
 + 
 +        self.score = 0 
 +        self.hiscore = 0 
 + 
 +    def update(self): 
 +        # Restrict to screen. 
 +        if self.rect.x < 0: 
 +            self.rect.x = 0 
 +        if self.rect.x > Screen.WIDTH - self.SIZE: 
 +            self.rect.x = Screen.WIDTH - self.SIZE 
 +        if self.rect.y < 0: 
 +            self.rect.y = 0 
 +        if self.rect.y > Screen.HEIGHT - self.SIZE: 
 +            self.rect.y = Screen.HEIGHT - self.SIZE 
 + 
 +        # Easier to read, but, slower. 
 +        #self.rect.x = max(0, min(self.rect.x, Screen.WIDTH - self.SIZE)) 
 +        #self.rect.y = max(0, min(self.rect.y, Screen.HEIGHT - self.SIZE)) 
 +</Code> 
 + 
 +=== Sound.py 
 +<Code:Python> 
 +import pygame as a 
 + 
 +class Sound: 
 +    def __init__(self): 
 +        a.mixer.init() 
 +        dir = 'assets/sound/' 
 +        self.sounds = {} 
 + 
 +        self.sounds["laser"] = a.mixer.Sound(dir+'laser1.wav'
 +        self.sounds["laser"].set_volume(0.15) 
 +        self.sounds["boom"] = a.mixer.Sound(dir+'boom6.wav'
 +        self.sounds["boom7"] = a.mixer.Sound(dir+'boom7.wav'
 +        self.sounds["start"] = a.mixer.Sound(dir+'start4.wav'
 +        self.sounds["pellet"] = a.mixer.Sound(dir+'pellet.wav'
 + 
 +    def play(self, sound): 
 +        if sound in self.sounds: 
 +            self.sounds[sound].play() 
 +        else: 
 +            print(f"There is no {sound} sound!"
 +</Code>
bullet_game.1725068826.txt.gz · Last modified: 2024/08/31 01:47 by appledog

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki