diff --git a/engine/math.py b/engine/math.py new file mode 100644 index 0000000..a767cf1 --- /dev/null +++ b/engine/math.py @@ -0,0 +1,33 @@ +""" +Définis des classes utiles. +""" + + +class Vec2: + """ + Un vecteur 2D + """ + + def __init__(self, x: float, y: float) -> None: + self.x = x + self.y = y + + def __add__(self, other: "Vec2") -> "Vec2": + return Vec2(self.x + other.x, self.y + other.y) + + def __sub__(self, other: "Vec2") -> "Vec2": + return Vec2(self.x - other.x, self.y - other.y) + + def __mul__(self, other: "Vec2") -> "Vec2": + return Vec2(self.x * other.x, self.y * other.y) + + def __div__(self, other: "Vec2") -> "Vec2": + return Vec2(self.x / other.x, self.y / other.y) + + def __eq__(self, other: object) -> bool: + if isinstance(other, Vec2): + return self.x == other.x and self.y == other.y + return False + + def __repr__(self) -> str: + return f"Vec2({self.x}, {self.y})" diff --git a/engine/plugins/pygame.py b/engine/plugins/pygame.py new file mode 100644 index 0000000..0b0806d --- /dev/null +++ b/engine/plugins/pygame.py @@ -0,0 +1,208 @@ +""" +Définit un plugin qui gère les évenements pygame. +""" + +from engine import * +import pygame + +from engine.math import Vec2 + + +class PygamePlugin(Plugin): + """ + Plugin qui gère les évenements pygame. + """ + + @staticmethod + def _initialize_pygame(world: World) -> None: + pygame.init() + + # Initialisation des ressources + world.set(Display(pygame.display.set_mode((640, 480)))) + world.set(Keyboard(set(), set(), set())) + world.set(Mouse(set(), set(), set(), Vec2(0.0, 0.0))) + + @staticmethod + def _check_events(world: World) -> None: + keys = world[Keyboard]._keys.copy() + keys_pressed: set[str] = set() + keys_released: set[str] = set() + + buttons = world[Mouse]._buttons.copy() + buttons_pressed: set[int] = set() + buttons_released: set[int] = set() + mouse_position = world[Mouse]._position + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + world[Game].stop() + elif event.type == pygame.KEYDOWN: + key_name = pygame.key.name(event.key) + keys.add(key_name) + keys_pressed.add(key_name) + elif event.type == pygame.KEYUP: + key_name = pygame.key.name(event.key) + keys.remove(key_name) + keys_released.add(key_name) + elif event.type == pygame.MOUSEBUTTONDOWN: + buttons.add(event.button) + buttons_pressed.add(event.button) + elif event.type == pygame.MOUSEBUTTONUP: + buttons.remove(event.button) + buttons_released.add(event.button) + elif event.type == pygame.MOUSEMOTION: + mouse_position = Vec2(float(event.pos[0]), float(event.pos[1])) + + world.set(Keyboard(keys, keys_pressed, keys_released)) + world.set(Mouse(buttons, buttons_pressed, buttons_released, mouse_position)) + + @staticmethod + def _terminate_pygame(world: World) -> None: + pygame.quit() + + def apply(self, game: Game) -> None: + """ + Applique le plugin a un jeu. + + Paramètres: + game: Le jeu auquel appliquer le plugin. + """ + game.add_startup_tasks(self._initialize_pygame) + game.add_pre_update_tasks(self._check_events) + game.add_shutdown_tasks(self._terminate_pygame) + + +class Display: + """ + Ressource qui represente la fenetre du jeu. + """ + + def __init__(self, surface: pygame.Surface) -> None: + self.surface = surface + + @property + def width(self) -> int: + """ + Renvoie la largeur de la fenetre + + Retourne: + La largeur de la fenetre + """ + return self.surface.get_width() + + @property + def height(self) -> int: + """ + Renvoie la hauteur de la fenetre + + Retourne: + La hauteur de la fenetre + """ + return self.surface.get_height() + + +class Keyboard: + """ + Ressource qui représente les entrées utilisateurs sur le clavier à la frame actuelle. + """ + + def __init__(self, keys: set[str], pressed: set[str], released: set[str]) -> None: + self._keys = keys + self._pressed = pressed + self._released = released + + def is_key_pressed(self, key_name: str) -> bool: + """ + Renvoie True si la touche *key_name* a commencé a être appuyée pendant la frame actuelle. + + Paramètres: + key_name: Le nom de la touche à tester. + + Retourne: + True si la touche *key_name* a commencé a être appuyée pendant la frame actuelle. + """ + return key_name in self._pressed + + def is_key(self, key_name: str) -> bool: + """ + Renvoie True si la touche *key_name* est actuellement appuyée. + + Paramètres: + key_name: Le nom de la touche à tester. + + Retourne: + True si la touche *key_name* est actuellement appuyée. + """ + return key_name in self._keys + + def is_key_released(self, key_name: str) -> bool: + """ + Renvoie True si la touche *key_name* a été relachée pendant la frame actuelle. + + Paramètres: + key_name: Le nom de la touche à tester. + + Retourne: + True si la touche *key_name* a été relachée pendant la frame actuelle. + """ + return key_name in self._released + + +class Mouse: + """ + Ressource qui représente l'état de la souris à la frame actuelle. + """ + + def __init__( + self, buttons: set[int], pressed: set[int], released: set[int], position: Vec2 + ) -> None: + self._buttons = buttons + self._pressed = pressed + self._released = released + self._position = position + + def is_button_pressed(self, button: int) -> bool: + """ + Renvoie True si le bouton *button* a commencé a être appuyée pendant la frame actuelle. + + Paramètres: + button: Le numéro du bouton à tester. + + Retourne: + True si le bouton *button* a commencé a être appuyée pendant la frame actuelle. + """ + return button in self._pressed + + def is_button(self, button: int) -> bool: + """ + Renvoie True si le bouton *button* est actuellement appuyé. + + Paramètres: + button: Le numéro du bouton à tester. + + Retourne: + True si le bouton *button* est actuellement appuyé. + """ + return button in self._buttons + + def is_button_released(self, button: int) -> bool: + """ + Renvoie True si le bouton *button* a été relaché pendant la frame actuelle. + + Paramètres: + button: Le numéro du bouton à tester. + + Retourne: + True si le bouton *button* aLongrightarrow relaché pendant la frame actuelle. + """ + return button in self._released + + @property + def position(self) -> Vec2: + """ + Renvoie la position de la souris. + + Retourne: + La position de la souris. + """ + return self._position diff --git a/main.py b/main.py index c9814a8..f02dab0 100644 --- a/main.py +++ b/main.py @@ -4,32 +4,28 @@ Ceci est un exemple de comment l'on peut utiliser le moteur du jeu. from engine import * -from engine.plugins.timing import TimePlugin, Time +from engine.plugins.timing import TimePlugin +from engine.plugins.pygame import Keyboard, Mouse, PygamePlugin # Initialisation -game = Game(TimePlugin()) +game = Game(TimePlugin(), PygamePlugin()) -# Ajout de tache au démarage (l'ordre d'ajout est important) -game.add_startup_tasks(lambda world: print("Hello first")) -game.add_startup_tasks(lambda world: print("Hello second")) -game.add_startup_tasks(lambda world: print("Hello third")) -game.add_startup_tasks(lambda world: print("Hello last")) -# Ajoute de tache au mise à jour (malgré le world[Game].stop(), la boucle termine les taches suivantes) -game.add_pre_update_tasks(lambda world: print("Pre Update")) -game.add_pre_update_tasks(lambda world: print(world[Time])) -game.add_update_tasks(lambda world: world[Game].stop()) -game.add_post_update_tasks(lambda world: print("Post Update")) +# On créer une tache pour tester si les plugins fonctionnent +def salutations(world: World) -> None: + """ + Affiche "Bonjour" si la touche B est pressé et "Au revoir" si la touche B est relachée. + """ + if world[Keyboard].is_key_pressed("b"): + print("Bonjour") -# Ajout de tache au rendu -game.add_render_tasks(lambda world: print("Render task 1")) -game.add_render_tasks(lambda world: print("Render task 2")) -game.add_render_tasks(lambda world: print("Render task 3")) + if world[Keyboard].is_key_released("b"): + print("Au revoir") -# Ajout de tache à la fin -game.add_shutdown_tasks(lambda world: print("Bye first")) -game.add_shutdown_tasks(lambda world: print("Bye second")) + +# On ajoute la tache de test +game.add_update_tasks(salutations) # On lance la boucle game.run() diff --git a/requirements.txt b/requirements.txt index 8120074..969ddee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ mypy -pylint \ No newline at end of file +pylint +pygame \ No newline at end of file