diff --git a/engine/__init__.py b/engine/__init__.py index f2384c1..8dd071e 100644 --- a/engine/__init__.py +++ b/engine/__init__.py @@ -264,15 +264,29 @@ class Game: *plugins: Les plugins a ajouter au jeu. """ self._running = False + self._pre_startup_tasks: list[Callable[[World], None]] = [] self._startup_tasks: list[Callable[[World], None]] = [] + self._post_startup_tasks: list[Callable[[World], None]] = [] self._pre_update_tasks: list[Callable[[World], None]] = [] self._update_tasks: list[Callable[[World], None]] = [] self._post_update_tasks: list[Callable[[World], None]] = [] + self._pre_render_tasks: list[Callable[[World], None]] = [] self._render_tasks: list[Callable[[World], None]] = [] + self._post_render_tasks: list[Callable[[World], None]] = [] + self._pre_shutdown_tasks: list[Callable[[World], None]] = [] self._shutdown_tasks: list[Callable[[World], None]] = [] + self._post_shutdown_tasks: list[Callable[[World], None]] = [] for plugin in plugins: plugin.apply(self) + def add_pre_startup_tasks(self, *tasks: Callable[[World], None]) -> None: + """ + Ajoute des taches qui s'executeront en premier avant le démarrage du jeu. + """ + if self._running: + raise RuntimeError("Cannot add a task while the loop is running") + self._pre_startup_tasks.extend(tasks) + def add_startup_tasks(self, *tasks: Callable[[World], None]) -> None: """ Ajoute des taches qui s'executeront au démarrage du jeu. @@ -284,6 +298,14 @@ class Game: raise RuntimeError("Cannot add a task while the loop is running") self._startup_tasks.extend(tasks) + def add_post_startup_tasks(self, *tasks: Callable[[World], None]) -> None: + """ + Ajoute des taches qui s'executeront en dernier après le démarrage du jeu. + """ + if self._running: + raise RuntimeError("Cannot add a task while the loop is running") + self._post_startup_tasks.extend(tasks) + def add_pre_update_tasks(self, *tasks: Callable[[World], None]) -> None: """ Ajoute des taches qui s'executeront au debut de chaque mise à jour du jeu. @@ -317,6 +339,17 @@ class Game: raise RuntimeError("Cannot add a task while the loop is running") self._post_update_tasks.extend(tasks) + def add_pre_render_tasks(self, *tasks: Callable[[World], None]) -> None: + """ + Ajoute des taches qui s'executeront au début de chaque mise à jour du jeu pour le rendu. + + Paramètres: + *tasks: Les taches à ajouter. + """ + if self._running: + raise RuntimeError("Cannot add a task while the loop is running") + self._pre_render_tasks.extend(tasks) + def add_render_tasks(self, *tasks: Callable[[World], None]) -> None: """ Ajoute des taches qui s'executeront après chaque mise à jour du jeu pour le rendu. @@ -328,6 +361,25 @@ class Game: raise RuntimeError("Cannot add a task while the loop is running") self._render_tasks.extend(tasks) + def add_post_render_tasks(self, *tasks: Callable[[World], None]) -> None: + """ + Ajoute des taches qui s'executeront après chaque mise à jour du jeu pour le rendu. + + Paramètres: + *tasks: Les taches à ajouter. + """ + if self._running: + raise RuntimeError("Cannot add a task while the loop is running") + self._post_render_tasks.extend(tasks) + + def add_pre_shutdown_tasks(self, *tasks: Callable[[World], None]) -> None: + """ + Ajoute des taches qui s'executeront au début de la fin de la boucle de jeu. + """ + if self._running: + raise RuntimeError("Cannot add a task while the loop is running") + self._pre_shutdown_tasks.extend(tasks) + def add_shutdown_tasks(self, *tasks: Callable[[World], None]) -> None: """ Ajoute des taches qui s'executeront à la fin de la boucle de jeu. @@ -339,6 +391,14 @@ class Game: raise RuntimeError("Cannot add a task while the loop is running") self._shutdown_tasks.extend(tasks) + def add_post_shutdown_tasks(self, *tasks: Callable[[World], None]) -> None: + """ + Ajoute des taches qui s'executeront à la fin de la fin de la boucle de jeu. + """ + if self._running: + raise RuntimeError("Cannot add a task while the loop is running") + self._post_shutdown_tasks.extend(tasks) + def run(self, world: World = World()) -> World: """ Lance la boucle de jeu. @@ -351,6 +411,14 @@ class Game: world.set(self) world.apply() + # On execute les taches de pré initialisation du monde + for task in self._pre_startup_tasks: + try: + task(world) + except Exception as e: + error(f"Error during pre-startup task: {e}") + world.apply() + # On execute les taches d'initialisation du monde for task in self._startup_tasks: try: @@ -359,6 +427,14 @@ class Game: error(f"Error during startup task: {e}") world.apply() + # On execute les taches de post initialisation du monde + for task in self._post_startup_tasks: + try: + task(world) + except Exception as e: + error(f"Error during post-startup task: {e}") + world.apply() + while self._running: # On execute les taches de pré mise à jour du monde for task in self._pre_update_tasks: @@ -392,6 +468,22 @@ class Game: error(f"Error during render task: {e}") world.apply() + # On execute les taches de fin de rendu du jeu + for task in self._post_render_tasks: + try: + task(world) + except Exception as e: + error(f"Error during post-render task: {e}") + world.apply() + + # On execute les taches de pré fin de boucle + for task in self._pre_shutdown_tasks: + try: + task(world) + except Exception as e: + error(f"Error during pre-shutdown task: {e}") + world.apply() + # On exécute les taches de fin du monde for task in self._shutdown_tasks: try: @@ -400,6 +492,14 @@ class Game: error(f"Error during shutdown task: {e}") world.apply() + # On execute les taches de post fin de boucle + for task in self._post_shutdown_tasks: + try: + task(world) + except Exception as e: + error(f"Error during post-shutdown task: {e}") + world.apply() + # On retourne le monde return world diff --git a/engine/math.py b/engine/math.py index a767cf1..3aca6dc 100644 --- a/engine/math.py +++ b/engine/math.py @@ -3,31 +3,71 @@ Définis des classes utiles. """ +import math + + class Vec2: """ Un vecteur 2D """ - def __init__(self, x: float, y: float) -> None: - self.x = x - self.y = y + def __init__(self, x: float = 0, y: float = 0) -> None: + self.x = float(x) + self.y = float(y) - def __add__(self, other: "Vec2") -> "Vec2": - return Vec2(self.x + other.x, self.y + other.y) + def __add__(self, other: object) -> "Vec2": + if isinstance(other, Vec2): + return Vec2(self.x + other.x, self.y + other.y) + elif isinstance(other, float): + return Vec2(self.x + other, self.y + other) + return Vec2(float("nan"), float("nan")) - def __sub__(self, other: "Vec2") -> "Vec2": - return Vec2(self.x - other.x, self.y - other.y) + def __sub__(self, other: object) -> "Vec2": + if isinstance(other, Vec2): + return Vec2(self.x - other.x, self.y - other.y) + elif isinstance(other, float): + return Vec2(self.x - other, self.y - other) + return Vec2(float("nan"), float("nan")) - def __mul__(self, other: "Vec2") -> "Vec2": - return Vec2(self.x * other.x, self.y * other.y) + def __mul__(self, other: object) -> "Vec2": + if isinstance(other, Vec2): + return Vec2(self.x * other.x, self.y * other.y) + elif isinstance(other, float): + return Vec2(self.x * other, self.y * other) + return Vec2(float("nan"), float("nan")) - def __div__(self, other: "Vec2") -> "Vec2": - return Vec2(self.x / other.x, self.y / other.y) + def __truediv__(self, other: object) -> "Vec2": + if isinstance(other, Vec2): + return Vec2(self.x / other.x, self.y / other.y) + elif isinstance(other, float): + return Vec2(self.x / other, self.y / other) + return Vec2(float("nan"), float("nan")) def __eq__(self, other: object) -> bool: if isinstance(other, Vec2): return self.x == other.x and self.y == other.y return False + def __hash__(self) -> int: + return hash((self.x, self.y)) + + def __neg__(self) -> "Vec2": + return Vec2(-self.x, -self.y) + + @property + def length(self) -> float: + """ + Retourne la longueur du vecteur. + """ + return math.sqrt(self.x**2 + self.y**2) + + @property + def normalized(self) -> "Vec2": + """ + Retourne une version normalisé du vecteur. + """ + length = self.length + return Vec2(self.x / length, self.y / length) + def __repr__(self) -> str: return f"Vec2({self.x}, {self.y})" diff --git a/engine/plugins/pygame.py b/engine/plugins/pygame.py index d07e5f1..8211549 100644 --- a/engine/plugins/pygame.py +++ b/engine/plugins/pygame.py @@ -3,9 +3,8 @@ Définit un plugin qui gère les évenements pygame. """ from engine import * -import pygame - from engine.math import Vec2 +import pygame class PygamePlugin(Plugin): @@ -14,14 +13,20 @@ class PygamePlugin(Plugin): """ @staticmethod - def _initialize_pygame(world: World) -> None: + def _initialize(world: World) -> None: pygame.init() # Initialisation des ressources - world.set(Display(pygame.display.set_mode((640, 480))), Keyboard(), Mouse()) + world.set( + Display(pygame.display.set_mode((800, 600), pygame.RESIZABLE)), + Keyboard(), + Mouse(), + ) @staticmethod def _check_events(world: World) -> None: + world[Display].size = Vec2(*pygame.display.get_surface().get_size()) + keyboard = world[Keyboard] keyboard.pressed.clear() keyboard.released.clear() @@ -51,7 +56,16 @@ class PygamePlugin(Plugin): mouse.position = Vec2(float(event.pos[0]), float(event.pos[1])) @staticmethod - def _terminate_pygame(world: World) -> None: + def _flip_display(world: World) -> None: + """ + Met a jour le rendu de l'écran. + """ + display = world[Display] + pygame.display.flip() + pygame.display.get_surface().fill((0, 0, 0)) + + @staticmethod + def _terminate(world: World) -> None: pygame.quit() def apply(self, game: Game) -> None: @@ -61,9 +75,10 @@ class PygamePlugin(Plugin): Paramètres: game: Le jeu auquel appliquer le plugin. """ - game.add_startup_tasks(self._initialize_pygame) + game.add_pre_startup_tasks(self._initialize) game.add_pre_update_tasks(self._check_events) - game.add_shutdown_tasks(self._terminate_pygame) + game.add_post_render_tasks(self._flip_display) + game.add_post_shutdown_tasks(self._terminate) class Display: @@ -71,28 +86,22 @@ class Display: Ressource qui represente la fenetre du jeu. """ - def __init__(self, surface: pygame.Surface) -> None: - self.surface = surface + def __init__(self, display: pygame.Surface) -> None: + self.size: Vec2 = Vec2(*display.get_size()) @property - def width(self) -> int: + def width(self) -> float: """ - Renvoie la largeur de la fenetre - - Retourne: - La largeur de la fenetre + Retourne la largeur de la fenetre. """ - return self.surface.get_width() + return self.size.x @property - def height(self) -> int: + def height(self) -> float: """ - Renvoie la hauteur de la fenetre - - Retourne: - La hauteur de la fenetre + Retourne la hauteur de la fenetre. """ - return self.surface.get_height() + return self.size.y class Keyboard: diff --git a/engine/plugins/timing.py b/engine/plugins/timing.py index cadc4be..c1b2117 100644 --- a/engine/plugins/timing.py +++ b/engine/plugins/timing.py @@ -24,7 +24,7 @@ class TimePlugin(Plugin): """ Applique le plugin a un jeu. """ - game.add_startup_tasks(self._initialize_time) + game.add_pre_startup_tasks(self._initialize_time) game.add_pre_update_tasks(self._update_time)