From dbcf18e5073a62b6d702a1c862d2bb8ee77eeea4 Mon Sep 17 00:00:00 2001 From: Giovani Date: Wed, 7 Jul 2021 14:49:04 -0400 Subject: [PATCH] wip!: add scenes to the game --- config.yaml | 1 + main.py | 2 +- resource/image/title_screen.png | Bin 0 -> 4160 bytes tetris/game.py | 95 ++++++++++++++++------------ tetris/scene.py | 33 ++++++++++ tetris/util.py | 106 ++++++++++++++++---------------- 6 files changed, 142 insertions(+), 95 deletions(-) create mode 100644 resource/image/title_screen.png create mode 100644 tetris/scene.py diff --git a/config.yaml b/config.yaml index df28b14..b41f887 100644 --- a/config.yaml +++ b/config.yaml @@ -10,6 +10,7 @@ sound: piece-set: "resource/sound/piece_set_3.wav" image: + title-screen: "resource/image/title_screen.png" window-icon: "resource/image/tetris_icon.png" font: "resource/image/press-start-2p-font.bmp" diff --git a/main.py b/main.py index 62bc0f2..391a183 100644 --- a/main.py +++ b/main.py @@ -8,7 +8,7 @@ from tetris.game import Game from tetris.util import ConfigurationManager def main() -> None: - ConfigurationManager.load() + ConfigurationManager. init() game = Game() game.initialize() diff --git a/resource/image/title_screen.png b/resource/image/title_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..c5f0a3539a9facd7c85b42a1aa5aa05566ed8bf7 GIT binary patch literal 4160 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 KMNUMnLSTZ7n;+8v literal 0 HcmV?d00001 diff --git a/tetris/game.py b/tetris/game.py index 64b7090..8086ccf 100644 --- a/tetris/game.py +++ b/tetris/game.py @@ -7,7 +7,9 @@ from tetris.entity import PieceGenerator from tetris.entity import Well from tetris.entity import Stack from tetris.online import MultiplayerService +from tetris.scene import TitleScene +# TODO improve game assets https://www.spriters-resource.com/nes/tetris/ # TODO should be a singleton and refactor the whole file? class Game: @@ -16,15 +18,19 @@ class Game: self.tile_size = -1 self.screen = None self.clock = None - self.main_music = None + + self.current_scene = TitleScene() + + # In Game # self.current_piece = None self.next_piece = None self.well = None self.stack = None + self.main_music = None def initialize(self) -> None: pygame.init() - TextGenerator.load(ConfigurationManager.get("image", "font"), (20, 20)) + TextGenerator.init(ConfigurationManager.get("image", "font"), (20, 20)) win_width = ConfigurationManager.get("window", "width") win_height = ConfigurationManager.get("window", "height") @@ -50,25 +56,29 @@ class Game: # gets called from the games main loop def update(self) -> None: - # TODO write not initialized exception - elapsed_time = self.clock.tick(self.fps) - if not self.next_piece: - self.next_piece = PieceGenerator.get_piece((620, 160)) - - if self.current_piece: - self.current_piece.update(elapsed_time, self) + if self.current_scene: + self.current_scene.update() else: - self.current_piece = self.next_piece - self.current_piece.move((360 - 620, 100 - 160)) # TODO calculate spawn position correctly - self.next_piece = PieceGenerator.get_piece((620, 160)) # (360, 100) - if self.stack and self.current_piece.collide(self.stack): # TODO game over redo - pygame.quit() - MultiplayerService.quit() - sys.exit() + # TODO write not initialized exception + elapsed_time = self.clock.tick(self.fps) - if self.stack: - self.stack.update(elapsed_time, self) + if not self.next_piece: + self.next_piece = PieceGenerator.get_piece((620, 160)) + + if self.current_piece: + self.current_piece.update(elapsed_time, self) + else: + self.current_piece = self.next_piece + self.current_piece.move((360 - 620, 100 - 160)) # TODO calculate spawn position correctly + self.next_piece = PieceGenerator.get_piece((620, 160)) # (360, 100) + if self.stack and self.current_piece.collide(self.stack): # TODO game over redo + pygame.quit() + MultiplayerService.quit() + sys.exit() + + if self.stack: + self.stack.update(elapsed_time, self) for event in pygame.event.get(): if event.type == pygame.QUIT: @@ -79,31 +89,34 @@ class Game: def draw(self) -> None: # TODO write not initialized exception - # draw window bg - bg_color = pygame.Color(ConfigurationManager.get("color", "window-bg")) - self.screen.fill(bg_color) + if self.current_scene: + self.current_scene.draw(self.screen) + else: + # draw window bg + bg_color = pygame.Color(ConfigurationManager.get("color", "window-bg")) + self.screen.fill(bg_color) - # draw all game objects - if self.next_piece: - self.next_piece.draw(self.screen) - if self.well: - self.well.draw(self.screen) - if self.stack: - self.stack.draw(self.screen) - if self.current_piece: - self.current_piece.draw(self.screen, self.well, self.stack) - - score = str(self.score).zfill(6) - lines = str(self.stack.lines_completed_count).zfill(4) - level = str(self.get_level()).zfill(2) + # draw all game objects + if self.next_piece: + self.next_piece.draw(self.screen) + if self.well: + self.well.draw(self.screen) + if self.stack: + self.stack.draw(self.screen) + if self.current_piece: + self.current_piece.draw(self.screen, self.well, self.stack) + + score = str(self.score).zfill(6) + lines = str(self.stack.lines_completed_count).zfill(4) + level = str(self.get_level()).zfill(2) - TextGenerator.draw("Top", (80, 120), self.screen) - TextGenerator.draw("000000", (80, 140), self.screen) - TextGenerator.draw("Score", (80, 180), self.screen) - TextGenerator.draw(score, (80, 200), self.screen) - TextGenerator.draw("Lines " + lines, (300, 40), self.screen) - TextGenerator.draw("Next", (600, 120), self.screen) - TextGenerator.draw("LVL " + level, (600, 220), self.screen) + TextGenerator.draw("Top", (80, 120), self.screen) + TextGenerator.draw("000000", (80, 140), self.screen) + TextGenerator.draw("Score", (80, 180), self.screen) + TextGenerator.draw(score, (80, 200), self.screen) + TextGenerator.draw("Lines " + lines, (300, 40), self.screen) + TextGenerator.draw("Next", (600, 120), self.screen) + TextGenerator.draw("LVL " + level, (600, 220), self.screen) # update display pygame.display.update() diff --git a/tetris/scene.py b/tetris/scene.py new file mode 100644 index 0000000..55478f3 --- /dev/null +++ b/tetris/scene.py @@ -0,0 +1,33 @@ +import pygame +from tetris.util import ConfigurationManager +from tetris.util import TextGenerator +from tetris.util import Controller + +""" + TODO +""" +class TitleScene: + + # Title screen options + ONE_PLAYER = "ONE_PLAYER" + TWO_PLAYER = "TWO_PLAYER" + + def __init__(self) -> None: + image_path = ConfigurationManager.get("image", "title-screen") + + self.splash_image = pygame.image.load(image_path) + self.cursor_position = None + self.cursor_flash_timer = 0 + + def draw(self, screen) -> None: + screen.fill(pygame.Color(ConfigurationManager.get("color", "window-bg"))) + screen.blit(self.splash_image, (300, 0)) + + TextGenerator.draw("1 PLAYER", (300, 400), screen) + TextGenerator.draw("2 PLAYER", (300, 450), screen) + + def update(self) -> None: + if Controller.key_pressed(pygame.K_DOWN): + self.current_option = TitleScene.ONE_PLAYER + if Controller.key_pressed(pygame.K_UP): + self.current_option = TitleScene.TWO_PLAYER \ No newline at end of file diff --git a/tetris/util.py b/tetris/util.py index 80ae5d8..ed5c943 100644 --- a/tetris/util.py +++ b/tetris/util.py @@ -8,96 +8,96 @@ from typing import KeysView, Tuple class ConfigurationManager: CONFIG_FILE_LOCATION = "config.yaml" - configuration = [] + _configuration = [] @classmethod - def load(cls) -> None: + def init(cls) -> None: with open(cls.CONFIG_FILE_LOCATION, "r") as yaml_file: - cls.configuration = yaml.safe_load(yaml_file) + cls._configuration = yaml.safe_load(yaml_file) @classmethod def get(cls, key: str, sub_key: str = None): if sub_key: - return cls.configuration[key][sub_key] + return cls._configuration[key][sub_key] else: - return cls.configuration[key] + return cls._configuration[key] """ TODO description """ class TextGenerator: - sheet = None - glyph_size = (-1, -1) - characters = { } + _sheet = None + _glyph_size = (-1, -1) + _characters = { } @classmethod - def load(cls, file: str, glyph_size: Tuple) -> None: - cls.sheet = pygame.image.load(file) - cls.glyph_size = glyph_size + def init(cls, file: str, glyph_size: Tuple) -> None: + cls._sheet = pygame.image.load(file) + cls._glyph_size = glyph_size # load character positions in bitmap into the characters dictionary # letters - cls.characters["A"] = (9 * glyph_size[0], 2 * glyph_size[1]) - cls.characters["B"] = (10 * glyph_size[0], 2 * glyph_size[1]) - cls.characters["C"] = (11 * glyph_size[0], 2 * glyph_size[1]) - cls.characters["D"] = (0 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["E"] = (1 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["F"] = (2 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["G"] = (3 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["H"] = (4 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["I"] = (5 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["J"] = (6 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["K"] = (7 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["L"] = (8 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["M"] = (9 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["N"] = (10 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["O"] = (11 * glyph_size[0], 3 * glyph_size[1]) - cls.characters["P"] = (0 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["Q"] = (1 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["R"] = (2 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["S"] = (3 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["T"] = (4 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["U"] = (5 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["V"] = (6 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["W"] = (7 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["X"] = (8 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["Y"] = (9 * glyph_size[0], 4 * glyph_size[1]) - cls.characters["Z"] = (10 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["A"] = (9 * glyph_size[0], 2 * glyph_size[1]) + cls._characters["B"] = (10 * glyph_size[0], 2 * glyph_size[1]) + cls._characters["C"] = (11 * glyph_size[0], 2 * glyph_size[1]) + cls._characters["D"] = (0 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["E"] = (1 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["F"] = (2 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["G"] = (3 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["H"] = (4 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["I"] = (5 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["J"] = (6 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["K"] = (7 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["L"] = (8 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["M"] = (9 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["N"] = (10 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["O"] = (11 * glyph_size[0], 3 * glyph_size[1]) + cls._characters["P"] = (0 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["Q"] = (1 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["R"] = (2 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["S"] = (3 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["T"] = (4 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["U"] = (5 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["V"] = (6 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["W"] = (7 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["X"] = (8 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["Y"] = (9 * glyph_size[0], 4 * glyph_size[1]) + cls._characters["Z"] = (10 * glyph_size[0], 4 * glyph_size[1]) # numbers - cls.characters["0"] = (4 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["1"] = (5 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["2"] = (6 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["3"] = (7 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["4"] = (8 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["5"] = (9 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["6"] = (10 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["7"] = (11 * glyph_size[0], 1 * glyph_size[1]) - cls.characters["8"] = (0 * glyph_size[0], 2 * glyph_size[1]) - cls.characters["9"] = (1 * glyph_size[0], 2 * glyph_size[1]) + cls._characters["0"] = (4 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["1"] = (5 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["2"] = (6 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["3"] = (7 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["4"] = (8 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["5"] = (9 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["6"] = (10 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["7"] = (11 * glyph_size[0], 1 * glyph_size[1]) + cls._characters["8"] = (0 * glyph_size[0], 2 * glyph_size[1]) + cls._characters["9"] = (1 * glyph_size[0], 2 * glyph_size[1]) @classmethod def draw(cls, text: str, position: Tuple, surface: pygame.Surface) -> None: x_position = 0 for char_ in text: if not char_.isspace(): - surface.blit(cls.sheet, (position[0] + x_position, position[1]), pygame.Rect(cls.characters[char_.upper()], (cls.glyph_size[0], cls.glyph_size[1]))) - x_position += cls.glyph_size[0] + surface.blit(cls._sheet, (position[0] + x_position, position[1]), pygame.Rect(cls._characters[char_.upper()], (cls._glyph_size[0], cls._glyph_size[1]))) + x_position += cls._glyph_size[0] """ TODO description """ class Controller: - keys_pressed = {} + _keys_pressed = {} @classmethod def key_down(cls, key: int) -> bool: - prior_pressed_state = False if key not in cls.keys_pressed else cls.keys_pressed[key] - cls.keys_pressed[key] = pygame.key.get_pressed()[key] + prior_pressed_state = False if key not in cls._keys_pressed else cls._keys_pressed[key] + cls._keys_pressed[key] = pygame.key.get_pressed()[key] - return cls.keys_pressed[key] and not prior_pressed_state + return cls._keys_pressed[key] and not prior_pressed_state @classmethod def key_pressed(cls, key: int) -> bool: