diff --git a/src/main.py b/src/main.py index 33f6a2d..3e3c67c 100644 --- a/src/main.py +++ b/src/main.py @@ -17,13 +17,13 @@ from plugins.render import ( from plugins.timing import Delta -def lol(a: Entity, b: Entity): +def lol(a: Entity, b: Entity, simul: bool = False): if Bounce in b: speed = a[physics.Velocity].length a[physics.Velocity] = a[physics.Velocity].normalized a[physics.Velocity].y = (a[Position].y - b[Position].y) * 0.005 a[physics.Velocity] = a[physics.Velocity].normalized * min( - (speed * 1.5), 1000.0 + (speed * 1.1), 1000.0 ) return True @@ -33,7 +33,7 @@ class Bounce: def lol_simul(a: Entity, b: Entity): - lol(a, b) + lol(a, b, True) return RightWall not in b @@ -161,6 +161,18 @@ def __initialize(world: World): physics.CollisionHandler(lol), ) + for i in range(20): + world.new_entity().set( + SpriteBundle( + "dodo.png", + 10, + scale=Vec2(10), + ), + physics.CollisionHandler(lol), + BallFollow(i + 1), + # physics.CollisionHandler(lol_simul), + ) + def __update(world: World): """ @@ -190,10 +202,21 @@ def __update(world: World): target = entity[Position].y for bar in world.query(Bar): diff = target - bar[Position].y - bar[Position].y += (diff / abs(diff)) * 300 * world[Delta] + # bar[Position].y += (diff / abs(diff)) * 300 * world[Delta] + bar[Position].y += diff bar.set(physics.Solid()) entity.destroy() + # for bar in world.query(Bar): + # bar.remove(physics.Solid) + for entity in world.query(BallFollow): + entity[Position] = Vec2(ball[Position]) + entity[physics.Velocity] = Vec2(ball[physics.Velocity]) + physics.move_entity(entity, entity[physics.Velocity] * entity[BallFollow] * 0.5) + del entity[physics.Velocity] + # for bar in world.query(Bar): + # bar.set(physics.Solid()) + # ball.set(Simulated()) # for entity in world.query(Bar): # entity.remove(physics.Solid) diff --git a/src/plugins save/__init__.py b/src/plugins save/__init__.py deleted file mode 100644 index 63ddc3a..0000000 --- a/src/plugins save/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -""" -Module contenant tous les plugins du jeu. - -Un plugin est une scène pouvant être ajouté a d'autres scènes -afin d'ajouter des fonctionnalités au jeu. - -Le but est de faire en sorte que les plugins soient génériques -afin de pouvoir les utilisers dans plusieurs scènes et donc -éviter de répéter plusieurs fois le même code. -""" diff --git a/src/plugins save/animation.py b/src/plugins save/animation.py deleted file mode 100644 index 9aa251a..0000000 --- a/src/plugins save/animation.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -Un plugin qui permet de jouer des animations de sprites. -""" - -from typing import Callable - -import pygame -from engine import GlobalPlugin -from engine.ecs import Entity, World -from plugins.assets import Assets -from plugins.render import Sprite -from plugins.timing import Delta - - -class Animation: - """ - Composant qui contient toutes les informations d'une animation est en cour - sur l'entité. - """ - - def __init__( - self, - name: str, - fps: float = 60.0, - loop: bool = False, - callback: Callable[[World, Entity], object] = lambda _w, _e: None, - ) -> None: - self.name = name - self.fps = fps - self.loop = loop - self.callback = callback - self.timer = 0.0 - self.ended = False - - def wait(self) -> Callable[[World], bool]: - """ - Utilitaire de `Coroutine` permettant d'attendre que l'animation soit finie. - """ - return lambda world: self.ended - - -def __update_sprite(entity: Entity, texture: pygame.Surface, assets: Assets): - """ - Change la texture de la `Sprite` d'une entité. - - Si l'entité n'as pas de `Sprite`, une nouvelle `Sprite` sera ajoutée. - Si la texture est la texture d'erreur, la texture de la `Sprite` ne - sera pas changée. - """ - if texture is assets.error_texture: - return - if Sprite not in entity: - entity[Sprite] = Sprite(texture) - else: - entity[Sprite].texture = texture - - -def __update_animations(world: World) -> None: - """ - Met à jour les sprites des animations. - """ - assets = world[Assets] - delta = world[Delta] - for entity in world.query(Animation): - animation = entity[Animation] - animation.timer += delta - - # On récupère la texture correspondante a la frame de l'animation - frame_index = int(animation.timer * animation.fps) - texture = assets.get_texture(f"{animation.name}/{frame_index:04d}") - - # Si la texture n'existe pas, l'animation est finie - if texture is assets.error_texture: - if animation.loop: - # Si l'animation est une boucle on met la première image de l'animation - animation.timer = 0.0 - __update_sprite( - entity, assets.get_texture(f"{animation.name}/0000"), assets - ) - - # Et on appelle la fonction de callback - animation.callback(world, entity) - else: - # Sinon on supprime le composant `Animation` - del entity[Animation] - animation.ended = True - - # On trouve la dernière image de l'animation et on met à jour la texture - while texture is assets.error_texture: - frame_index -= 1 - texture = assets.get_texture(f"{animation.name}/{frame_index:04d}") - if frame_index < 0: - break - if texture is not assets.error_texture: - __update_sprite(entity, texture, assets) - - # Et on appelle la fonction de callback - animation.callback(world, entity) - else: - # Si l'animation n'est pas finie, on met à jour la texture - __update_sprite(entity, texture, assets) - animation.ended = False - - -PLUGIN = GlobalPlugin( - [], - [], - [__update_animations], - [], -) diff --git a/src/plugins save/assets.py b/src/plugins save/assets.py deleted file mode 100644 index 8adf9f9..0000000 --- a/src/plugins save/assets.py +++ /dev/null @@ -1,271 +0,0 @@ -""" -Un plugin qui gère les assets du jeu. -""" - -import glob -import random -import pygame -from engine import CurrentScene, GlobalPlugin, KeepAlive, Scene -from engine.ecs import World -from plugins import render -from plugins.sound import Sound - - -class Assets(KeepAlive): - """ - Ressource qui gère les assets du jeu. - """ - - def __init__(self): - # Création de la texture d'erreur - error_texture = pygame.Surface((256, 256)) - error_texture.fill((0, 0, 0)) - pygame.draw.rect(error_texture, (255, 0, 255), (0, 0, 128, 128)) - pygame.draw.rect(error_texture, (255, 0, 255), (128, 128, 128, 128)) - self.__error_texture = error_texture.convert() - - # Chragement du son d'erreur - self.__error_sound = pygame.mixer.Sound("assets/error.mp3") - # self.__waiting_sound = pygame.mixer.Sound("assets/waiting.mp3") - - # Chargement des textures de chargement - # self.__unloaded_texture = pygame.image.load("assets/unloaded.png").convert() - # self.__loaded_texture = pygame.image.load("assets/loaded.png").convert() - - # Cache des ressources - self.__textures: dict[str, pygame.Surface] = {} - self.__fonts: dict[int, pygame.font.Font] = {} - self.__texts: dict[tuple[int, str], pygame.Surface] = {} - self.__sounds: dict[str, pygame.mixer.Sound] = {} - - @property - def error_texture(self) -> pygame.Surface: - """ - La texture d'erreur. - - Cette texture est utilisé lorsque la texture demandée n'existe pas. - """ - return self.__error_texture - - @property - def error_sound(self) -> pygame.mixer.Sound: - """ - Le son d'erreur. - - Cette texture est utilisé lorsque le son demandé n'existe pas. - """ - return self.__error_sound - - @property - def waiting_sound(self) -> pygame.mixer.Sound: - """ - Le son de chargement. - """ - return self.__waiting_sound - - @property - def unloaded_texture(self) -> pygame.Surface: - """ - La texture de chargement qui s'affiche au début du chargement et qui - est progressivement remplacé par la texture `loaded_texture`. - """ - return self.__unloaded_texture - - @property - def loaded_texture(self) -> pygame.Surface: - """ - La texture de chargement qui s'affiche progressivement lors d'un chargement. - """ - return self.__loaded_texture - - def load_texture(self, name: str, path: str) -> pygame.Surface: - """ - Charge une texture et la renvoi. Si une texture existe déja dans le cache, - elle sera remplacée par la nouvelle. - """ - surface = pygame.image.load(path).convert_alpha() - self.__textures[name] = surface - return surface - - def get_texture(self, name: str) -> pygame.Surface: - """ - Renvoie la texture demandée. - - Si la texture n'existe pas dans le cache, la texture d'erreur sera renvoyée. - """ - return self.__textures.get(name, self.__error_texture) - - def get_font(self, size: int) -> pygame.font.Font: - """ - Renvoie la police d'ecriture du jeu avec la taille demandée. - - Cette fonction charge le fichier `assets/font.ttf` pour la taille demandée - et la met dans le cache. Si la police d'ecriture existe déjà dans le cache, - elle sera renvoyée directement. - """ - font = self.__fonts.get(size) - if font is None: - font = self.__fonts[size] = pygame.font.Font("assets/font.ttf", size) - return font - - def get_text(self, size: int, text: str, color: pygame.Color) -> pygame.Surface: - """ - Renvoie une image correspondant à la chaîne de caractères demandée avec - la taille de police demandée et la couleur demandée. - - Si l'image du texte demandé n'est pas dans le cache, elle sera créer - puis mis dans le cache et enfin renvoyée. - """ - surface = self.__texts.get((size, text)) - if surface is None: - surface = self.__texts[(size, text)] = self.get_font(size).render( - text, True, color - ) - return surface - - def load_sound(self, name: str, path: str) -> pygame.mixer.Sound: - """ - Charge un son et le renvoi. Si un son existe déja dans le cache, - il sera remplacé par le nouveau. - """ - sound = pygame.mixer.Sound(path) - self.__sounds[name] = sound - return sound - - def get_sound(self, name: str) -> pygame.mixer.Sound: - """ - Renvoie le son demandé. - - Si le son n'existe pas dans le cache, le son d'erreur sera renvoyé. - """ - return self.__sounds.get(name, self.__error_sound) - - def clear_cache(self): - """ - Vide le cache des assets. - - Les fonts ne sont pas effacés car ils n'y a normalement pas énormément - de taille de police différentes utilisées. - """ - self.__textures.clear() - self.__texts.clear() - self.__sounds.clear() - - -def __initialize(world: World): - """ - Ajoute la ressource `Assets` au monde. - """ - world.set(Assets()) - - -PLUGIN = GlobalPlugin( - [__initialize], - [], - [], - [], -) - - -def loading_scene(target: Scene, name: str, clear_cache: bool = True): - """ - Retourne une scène de chargement des assets qui passe à la scène donné - en paramètres lorsque tous les assets de la scène sont chargées. - - Paramètres: - - `target`: la scène qui sera lancé après le chargement des assets. - - `name`: le nom de la scène, ce nom est utilisé pour savoir dans quel - dossier sont les assets de la scène. Les assets de la scène - seront récupéré dans le dossier `assets/`. - """ - - class AssetIterator: - """ - Une ressource qui contient un itérateur sur les fichiers des assets - de la scène à charger. - """ - - def __init__(self): - self.files = glob.glob(f"assets/{name}/**/*", recursive=True) - self.files.extend(glob.glob("assets/global/**/*", recursive=True)) - random.shuffle(self.files) - self.total = len(self.files) - - @staticmethod - def prepare_world(world: World): - """ - Retire toutes les ressource précédentes du monde puis - ajoute `ResourceIterator` et la barre de progression dans le monde. - """ - assets = world[Assets] - if clear_cache: - assets.clear_cache() - asset_iterator = AssetIterator() - world.set(AssetIterator()) - if asset_iterator.total <= 30: - for _ in range(asset_iterator.total): - asset_iterator.load_next(world) - else: - world.new_entity().set( - render.Sprite(assets.unloaded_texture, order=1000000000) - ) - world.new_entity().set( - ProgessBar(), - render.Sprite( - assets.loaded_texture, - order=1000000001, - area=(0, 0, 0, render.HEIGHT), - ), - Sound(assets.waiting_sound, loop=True), - ) - - @staticmethod - def load_next(world: World): - """ - Charge le fichier suivant de l'itérateur. - """ - assets = world[Assets] - asset_iterator = world[AssetIterator] - if len(asset_iterator.files) == 0: - world[CurrentScene] = target - else: - file = asset_iterator.files.pop().replace("\\", "/") - ressource_extension = file.split(".")[-1] - prefix = ( - "assets/global/" - if file.startswith("assets/global/") - else f"assets/{name}/" - ) - ressource_name = file[len(prefix) : -len(ressource_extension) - 1] - if ressource_extension in ("png", "jpg"): - assets.load_texture(ressource_name, file) - if ressource_extension in ("mp3", "wav", "ogg"): - assets.load_sound(ressource_name, file) - - class ProgessBar: - """ - Composant marquant une entité comme étant une barre de progression. - """ - - @staticmethod - def render(world: World): - """ - Affiche une barre de progression du chargement des assets. - """ - # Calcul du pourcentage de chargement - asset_iterator = world[AssetIterator] - if asset_iterator.total == 0: - progress = 0.0 - else: - file_loaded = asset_iterator.total - len(asset_iterator.files) - progress = file_loaded / asset_iterator.total - - # Affichage de la barre de progression - for progress_bar in world.query(ProgessBar): - progress_bar[render.Sprite].area = (0, 0, progress, 1.0) - - return Scene( - [AssetIterator.prepare_world], - [AssetIterator.load_next, ProgessBar.render], - [], - ) diff --git a/src/plugins save/click.py b/src/plugins save/click.py deleted file mode 100644 index d4548fa..0000000 --- a/src/plugins save/click.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Un plugin permettant de savoir si l'on a cliqué sur une entité. -""" - -from typing import Callable -from engine import GlobalPlugin -from engine.ecs import Entity, World -from plugins.hover import Hovered -from plugins.inputs import Pressed -from plugins.render import Sprite - - -class Clicked: - """ - Component ajouté a toutes les entitées qui viennent d'être cliqué. - """ - - -class Clickable: - """ - Composant qui permet d'executer une fonction lorsqu'une entité est cliquee. - """ - - def __init__(self, callback: Callable[[World, Entity], object]): - self.callback = callback - - -def __update_clicked(world: World): - """ - Met à jour les composants `Clicked`. - """ - mouse_click = "button_1" in world[Pressed] - sprite_entities = world.query(Sprite) - for entity in sprite_entities: - if Hovered in entity and mouse_click: - entity[Clicked] = Clicked() - if Clickable in entity: - entity[Clickable].callback(world, entity) - else: - del entity[Clicked] - - -PLUGIN = GlobalPlugin( - [], - [__update_clicked], - [], - [], -) diff --git a/src/plugins save/coroutine.py b/src/plugins save/coroutine.py deleted file mode 100644 index 6b30659..0000000 --- a/src/plugins save/coroutine.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -Plugin permettant d'executer des coroutine. - -Une coroutine est une fonction qui est executée sur plusieurs frames, -cela permet d'executer du code comme si il s'agissait d'une fonction -classique tout en laissant tourner la boucle du jeu. Il est a noter que -cette fonction n'est pas lancé dans une autre thread, la fonction est -executé dans le thread principal, mais lorsqu'un `yield` est utilisé, -l'execution de la fonction est arrêté jusqu'a ce que la condition donné -par le `yield` soit remplie, ce qui permet au thread principal de pouvoir -continuer de faire le rendu et la logique du jeu. Faire un traivail bloquant -dans une coroutine auras donc un impacte sur le nombre d'images par secondes -du jeu. -""" - - -from time import time -from typing import Callable, Generator, Optional -from engine import GlobalPlugin -from engine.ecs import Entity, World -from plugins.timing import GlobalTime - - -def wait(seconds: float) -> Callable[[World], bool]: - """ - Utilitaire de `Coroutine` permettant d'attendre un certain temps. - """ - stop_time = time() + seconds - return lambda world: world[GlobalTime] >= stop_time - - -def condition(condition_function: Callable[[World], bool]) -> Callable[[World], bool]: - """ - Utilitaire de `Coroutine` permettant d'attendre que la condition soit réalisé avant - de continuer l'execution. - """ - return condition_function - - -class Coroutine: - """ - Composant permettant d'executer une coroutine. - """ - - def __init__(self, generator: Generator[Callable[[World], bool], None, None]): - self.generator = generator - self.__condition: Optional[ # pylint: disable=unused-private-member - Callable[[World], bool] - ] = None - - @staticmethod - def update(entity: Entity): - """ - Met à jour la coroutine d'une entité. - - Si l'entité n'as pas de `Coroutine` la fonction ne fait rien. - """ - if Coroutine not in entity: - return - coroutine = entity[Coroutine] - if coroutine.__condition is None: - coroutine.__condition = next( # pylint: disable=unused-private-member - coroutine.generator, None - ) - if coroutine.__condition is None: - del entity[Coroutine] - return - if coroutine.__condition(entity.world): - coroutine.__condition = None # pylint: disable=unused-private-member - - -def __update_coroutines(world: World): - """ - Met à jour les coroutine du jeu. - """ - for entity in world.query(Coroutine): - Coroutine.update(entity) - - -PLUGIN = GlobalPlugin( - [], - [__update_coroutines], - [], - [], -) diff --git a/src/plugins save/defaults.py b/src/plugins save/defaults.py deleted file mode 100644 index 8545e55..0000000 --- a/src/plugins save/defaults.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Plugin qui rassemple tous les plugins globaux. -""" - -from plugins import ( - animation, - assets, - click, - coroutine, - display, - hover, - inputs, - multisound, - render, - sound, - text, - timing, -) - - -PLUGIN = ( - display.PLUGIN - + timing.PLUGIN - + assets.PLUGIN - + inputs.PLUGIN - + hover.PLUGIN - + click.PLUGIN - + coroutine.PLUGIN - + multisound.PLUGIN - + sound.PLUGIN - + text.PLUGIN - + animation.PLUGIN - + render.PLUGIN -) diff --git a/src/plugins save/display.py b/src/plugins save/display.py deleted file mode 100644 index cc0c1c2..0000000 --- a/src/plugins save/display.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Un plugin pour la gestion de la fenetre du jeu. -""" - - -import pygame -from engine.ecs import World -from engine import GlobalPlugin - - -def __initialize(_world: World): - """ - Initialise pygame et les ressources pour la gestion de la fenetre. - """ - pygame.init() - pygame.display.set_caption("Guess The Number") - pygame.display.set_mode((800, 600), pygame.RESIZABLE) - - -def __terminate(_world: World): - """ - Arrête pygame. - """ - pygame.quit() - - -PLUGIN = GlobalPlugin( - [__initialize], - [], - [], - [__terminate], -) diff --git a/src/plugins save/hover.py b/src/plugins save/hover.py deleted file mode 100644 index c73ea65..0000000 --- a/src/plugins save/hover.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -Un plugin permettant de savoir si la souris est par dessus une entité. -""" - - -import pygame - -from engine import GlobalPlugin -from engine.ecs import World -from engine.math import Vec2 -from plugins.inputs import MousePosition -from plugins.render import Sprite - - -class HoverEnter: - """ - Composant indicant que l'entité vient de commencer a être survolée par la souris. - """ - - -class Hovered: - """ - Composant indicant que l'entité est survolée par la souris. - """ - - -class HoverExit: - """ - Composant indicant que la souris viens d'arreter de survoler l'entité. - """ - - -class HoveredTexture: - """ - Composant permettant de changer la texture d'une entité lorsque - celle-ci est survolée par la souris. - """ - - def __init__(self, normal: pygame.Surface, hovered: pygame.Surface): - self.normal = normal - self.hovered = hovered - - -def __update_hovered(world: World): - """ - Vérifie le survol de la souris sur les entitées. - """ - # On met à jour les composants - mouse_position = world[MousePosition] - for entity in world.query(Sprite): - # Récupération de la position et taille de l'entité - sprite = entity[Sprite] - size = Vec2(*sprite.texture.get_size()) * Vec2(*sprite.area[2:]) - position = sprite.position - (sprite.origin * size) - - # On détermine si la souris est sur l'entité - if ( - mouse_position.x >= position.x - and mouse_position.x <= position.x + size.x - and mouse_position.y >= position.y - and mouse_position.y <= position.y + size.y - ): - if Hovered not in entity: - entity[HoverEnter] = HoverEnter() - else: - del entity[HoverEnter] - entity[Hovered] = Hovered() - else: - if Hovered in entity: - entity[HoverExit] = HoverExit() - else: - del entity[HoverExit] - del entity[Hovered] - - # On affiche la bonne texture - for entity in world.query(HoveredTexture): - if Hovered in entity: - texture = entity[HoveredTexture].hovered - else: - texture = entity[HoveredTexture].normal - if Sprite in entity: - entity[Sprite].texture = texture - else: - entity[Sprite] = Sprite(texture) - - -PLUGIN = GlobalPlugin( - [], - [__update_hovered], - [], - [], -) diff --git a/src/plugins save/inputs.py b/src/plugins save/inputs.py deleted file mode 100644 index 7c7f59b..0000000 --- a/src/plugins save/inputs.py +++ /dev/null @@ -1,90 +0,0 @@ -""" -Un plugin permettant de gérer les entrées utilisateur du jeu. -""" - -import pygame -from engine import CurrentScene, GlobalPlugin, KeepAlive -from engine.ecs import World -from engine.math import Vec2 -from plugins import render - - -class Pressed(KeepAlive, set[str]): - """ - Ressource qui correspond aux touches qui viennent d'être préssées par l'utilisateur. - - Pour les boutons de la souris la touche sera nommée `button_x` avec `x` le numéro du bouton. - """ - - -class Held(KeepAlive, set[str]): - """ - Ressource qui correspond aux touches qui sont actuellement préssées par l'utilisateur. - """ - - -class Released(KeepAlive, set[str]): - """ - Ressource qui correspond aux touches qui viennent d'être relachées par l'utilisateur. - """ - - -class MousePosition(KeepAlive, Vec2): - """ - Ressource qui correspond à la position de la souris. - """ - - -def __initialize(world: World): - """ - Initialise des ressources pour la gestion des entrées utilisateur. - """ - world.set(Pressed(), Held(), Released(), MousePosition()) - - -def __update_input(world: World): - """ - Met à jour les ressources qui permettent de savoir quels sont les - entrées utilisateur. - """ - # On récupère les ressources - pressed = world[Pressed] - held = world[Held] - released = world[Released] - - # On clear les touches pressées et relachées - pressed.clear() - released.clear() - - # On récupère les évenements de pygame - for event in pygame.event.get(): - if event.type == pygame.QUIT: - del world[CurrentScene] - elif event.type == pygame.KEYDOWN: - key_name = pygame.key.name(event.key) - held.add(key_name) - pressed.add(key_name) - elif event.type == pygame.KEYUP: - key_name = pygame.key.name(event.key) - held.remove(key_name) - released.add(key_name) - elif event.type == pygame.MOUSEBUTTONDOWN: - held.add(f"button_{event.button}") - pressed.add(f"button_{event.button}") - elif event.type == pygame.MOUSEBUTTONUP: - held.remove(f"button_{event.button}") - released.add(f"button_{event.button}") - elif event.type == pygame.MOUSEMOTION: - x, y, w, h = render.calculate_surface_rect() - world[MousePosition] = Vec2( - ((event.pos[0] - x) / w) * render.WIDTH, - ((event.pos[1] - y) / h) * render.HEIGHT, - ) - - -PLUGIN = GlobalPlugin( - [__initialize], - [__update_input], - [], - [], -) diff --git a/src/plugins save/multisound.py b/src/plugins save/multisound.py deleted file mode 100644 index f42e44b..0000000 --- a/src/plugins save/multisound.py +++ /dev/null @@ -1,65 +0,0 @@ -""" -Plugin pour les sons multiples. -""" - - -import random -from typing import Callable -from engine import GlobalPlugin - -from engine.ecs import Entity, World -from plugins.assets import Assets -from plugins.sound import Sound - - -class MultiSound: - """ - Composant qui quand il est ajouté a une entité, il joue un son aléatoire - parmis la liste de ses sons, il est ensuite retiré de l'entité. - - Ce composant est fait pour être définis en tant que constante - puis utilisé ensuite, pour cela il prend seulement le nom des - sons au lieu des sons eux memes. Les sons seront donc récupérés - a l'aide de `Assets`. - """ - - def __init__( - self, - *sound_names: str, - loop: bool = False, - volume: float = 1.0, - fade_ms: int = 0, - callback: Callable[[World, Entity], object] = lambda _w, _e: None, - ): - self.sound_names = sound_names - self.loop = loop - self.volume = volume - self.fade_ms = fade_ms - self.callback = callback - - -def __update_sounds(world: World): - """ - Met à jour les sons du jeu. - """ - # Ajout des sons aléatoires - assets = world[Assets] - for entity in world.query(MultiSound): - multi_sound = entity[MultiSound] - sound = assets.get_sound(random.choice(multi_sound.sound_names)) - entity[Sound] = Sound( - sound, - multi_sound.loop, - multi_sound.volume, - multi_sound.fade_ms, - multi_sound.callback, - ) - del entity[MultiSound] - - -PLUGIN = GlobalPlugin( - [], - [], - [__update_sounds], - [], -) diff --git a/src/plugins save/render.py b/src/plugins save/render.py deleted file mode 100644 index f8aec72..0000000 --- a/src/plugins save/render.py +++ /dev/null @@ -1,106 +0,0 @@ -""" -Un plugin qui s'occupe de rendre des choses dans la fenetre. -""" - -from typing import Optional -import pygame -from engine import GlobalPlugin, KeepAlive -from engine.ecs import World -from engine.math import Vec2 - - -WIDTH = 1440 -HEIGHT = 1080 -RATIO = WIDTH / HEIGHT -INVERT_RATIO = HEIGHT / WIDTH - - -def calculate_surface_rect() -> tuple[float, float, float, float]: - """ - Calcule et renvoie un rectangle qui corresponed a la zone dans laquelle le jeu est rendu. - """ - width, height = pygame.display.get_surface().get_size() - if width / height < RATIO: - target_height = width * INVERT_RATIO - offset = (height - target_height) / 2 - return 0.0, offset, float(width), target_height - target_width = height * RATIO - offset = (width - target_width) / 2 - return offset, 0.0, target_width, float(height) - - -class Surface(KeepAlive, pygame.Surface): - """ - Ressource qui stocke la surface de rendu du jeu. - """ - - -class Sprite: - """ - Composant donnant la texture d'une entité, sa position et son ordre de rendu. - """ - - def __init__( - self, - texture: pygame.Surface, - position: Vec2 = Vec2(0), - order: float = -1.0, - area: Optional[tuple[float, float, float, float]] = None, - origin: Vec2 = Vec2(0), - ): - self.texture = texture - self.position = position - self.order = order - if area is None: - self.area = (0.0, 0.0, 1.0, 1.0) - else: - self.area = area - self.origin = origin - - -def __initialize(world: World): - """ - Prépare le monde pour la gestion du rendu. - """ - world.set(Surface((WIDTH, HEIGHT))) - - -def __render(world: World): - """ - Rend le monde du jeu sur la surface puis l'affiche sur la fenetre. - """ - # On rend le monde sur la surface - surface = world[Surface] - sprites = [entity[Sprite] for entity in world.query(Sprite)] - for sprite in sorted(sprites, key=lambda sprite: sprite.order): - original_size = Vec2(*sprite.texture.get_size()) - size = original_size * Vec2(*sprite.area[2:]) - position = sprite.position - (sprite.origin * size) - surface.blit( - sprite.texture, - (position.x, position.y), - ( - sprite.area[0] * original_size.x, - sprite.area[1] * original_size.y, - sprite.area[2] * original_size.x, - sprite.area[3] * original_size.y, - ), - ) - - # On affiche la surface sur la fenetre - rect = calculate_surface_rect() - pygame.transform.set_smoothscale_backend("MMX") - pygame.transform.smoothscale( - surface, - (rect[2], rect[3]), - pygame.display.get_surface().subsurface(rect), - ) - pygame.display.flip() - - -PLUGIN = GlobalPlugin( - [__initialize], - [], - [__render], - [], -) diff --git a/src/plugins save/smooth.py b/src/plugins save/smooth.py deleted file mode 100644 index 051f9eb..0000000 --- a/src/plugins save/smooth.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Un plugin permettant de faire des déplacements fluides des entités. -""" - -from engine import Plugin, World -from engine.math import Vec2 -from plugins.render import Sprite -from plugins.timing import Delta - - -class Target(Vec2): - """ - Composant donnant la position voulue de l'entité. - """ - - -class Speed(float): - """ - Composant donnant la vittesse de déplacement de l'entité. - """ - - -def __update_positions(world: World): - """ - Met à jour la position des entités pour se rapprocher de la position voulue. - """ - for entity in world.query(Sprite, Target): - sprite = entity[Sprite] - target = entity[Target] - speed = entity[Speed] if Speed in entity else Speed(10) - sprite.position = ( - sprite.position + (target - sprite.position) * world[Delta] * speed - ) - - -PLUGIN = Plugin( - [], - [__update_positions], - [], -) diff --git a/src/plugins save/text.py b/src/plugins save/text.py deleted file mode 100644 index 8d08c5c..0000000 --- a/src/plugins save/text.py +++ /dev/null @@ -1,65 +0,0 @@ -""" -Un plugin permettant d'afficher du texte à l'écran. -""" - - -from typing import Optional -import pygame - -from engine import GlobalPlugin -from engine.ecs import World -from engine.math import Vec2 -from plugins.assets import Assets -from plugins.render import Sprite - - -class Text: - """ - Composant donnant le texte d'une entité, sa taille et sa couleur. - """ - - def __init__( - self, - text: str, - size: int = 50, - color: pygame.Color = pygame.Color(255, 255, 255), - position: Optional[Vec2] = None, - order: Optional[float] = None, - origin: Optional[Vec2] = None, - ): - self.text = text - self.size = size - self.color = color - self.position = position - self.order = order - self.origin = origin - - -def __render_texts(world: World): - """ - Rend les textes à l'écran. - - Pour rendre les textes, on moddifie le composant `Sprite` des entités. - """ - assets = world[Assets] - for entity in world.query(Text): - text = entity[Text] - texture = assets.get_text(text.size, text.text, text.color) - if Sprite in entity: - entity[Sprite].texture = texture - else: - entity[Sprite] = Sprite(texture) - if text.position is not None: - entity[Sprite].position = text.position - if text.order is not None: - entity[Sprite].order = text.order - if text.origin is not None: - entity[Sprite].origin = text.origin - - -PLUGIN = GlobalPlugin( - [], - [], - [__render_texts], - [], -) diff --git a/src/plugins save/timing.py b/src/plugins save/timing.py deleted file mode 100644 index cfdbdcf..0000000 --- a/src/plugins save/timing.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -Un plugin permettant de connaitre le temps depuis le -lancement du jeu et le temps depuis la dernière frame. -""" - - -from time import time -from engine import GlobalPlugin, KeepAlive -from engine.ecs import World - - -class GlobalTime(KeepAlive, float): - """ - Ressource qui représente le temps global de l'ordinateur sur lequel tourne le jeu. - """ - - -class Time(KeepAlive, float): - """ - Ressource qui représente le temps depuis le lancement du jeu. - """ - - -class Delta(KeepAlive, float): - """ - Ressource qui détermine le temps depuis la première frame. - """ - - -def __initialize(world: World): - """ - Initialise les ressources pour la gestion du temps. - """ - world.set(GlobalTime(time()), Time(0.0)) - - -def __update(world: World): - """ - Met à jour les ressources de temps. - """ - now = time() - world[Delta] = delta = now - world[GlobalTime] - world[GlobalTime] = now - world[Time] += delta - - -PLUGIN = GlobalPlugin( - [__initialize], - [__update], - [], - [], -) diff --git a/src/plugins/render.py b/src/plugins/render.py index 299b450..1827d77 100644 --- a/src/plugins/render.py +++ b/src/plugins/render.py @@ -2,6 +2,7 @@ Un plugin qui s'occupe de rendre des choses dans la fenetre. """ +from typing import Optional import pygame from engine import GlobalPlugin from engine.ecs import World @@ -39,9 +40,12 @@ class SpriteBundle: texture: str, order: float, position: Vec2 = Vec2(0), - scale: Vec2 = Vec2(128), + scale: Optional[Vec2] = None, origin: Vec2 = Vec2(0), ): + if scale is None: + surface = assets.load_texture(texture) + scale = Vec2(surface.get_width(), surface.get_height()) return ( Texture(texture), Order(order), diff --git a/src/scenes/menu.py b/src/scenes/menu.py deleted file mode 100644 index 8256c88..0000000 --- a/src/scenes/menu.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -La scène du menu principal du jeu. - -Dans cette scène nous pouvons choisir le mode de jeu. -""" -from scenes import CLICK_SOUND, one_player -from engine import CurrentScene, KeepAlive, Scene -from engine.ecs import Entity, World -from engine.math import Vec2 -from plugins import render -from plugins import assets as plugin_assets -from plugins.assets import Assets -from plugins.click import Clickable -from plugins.hover import HoveredTexture -from plugins.render import Sprite - - -def __create_button(world: World, assets: Assets, i: int, name: str): - """ - Ajoute un bouton au monde. - """ - world.new_entity().set( - Sprite( - assets.error_texture, - Vec2(500 + 450 * i, render.HEIGHT / 2), - 1, - origin=Vec2(0.5), - ), - HoveredTexture( - assets.get_texture(f"button_{name}"), - assets.get_texture(f"button_{name}_hover"), - ), - Clickable(lambda world, entity: __on_click_butons(world, entity, name)), - ) - - -def __on_click_butons(world: World, _entity: Entity, name: str): - """ - Fonction qui s'execute quand on clique sur un bouton. - """ - match name: - case "one_player": - world[CurrentScene] = one_player.SCENE - case "two_player": - pass - case _: - pass - world.new_entity().set(KeepAlive(), CLICK_SOUND) - - -def __spawn_elements(world: World): - """ - Ajoute les éléments du menu dans le monde. - """ - assets = world[Assets] - world.new_entity().set(Sprite(assets.get_texture("background"))) - scenes_name = ["one_player", "two_player"] - for i, name in enumerate(scenes_name): - __create_button(world, assets, i, name) - - -SCENE = plugin_assets.loading_scene( - Scene( - [__spawn_elements], - [], - [], - ), - "menu", -) diff --git a/src/scenes/one_player.py b/src/scenes/one_player.py deleted file mode 100644 index 834e9ff..0000000 --- a/src/scenes/one_player.py +++ /dev/null @@ -1,22 +0,0 @@ -from engine import Scene -from engine.ecs import World -from plugins import assets as plugin_assets -from plugins.render import Sprite - - -def __spawn_elements(world: World): - """ - Ajoute les éléments du menu dans le monde. - """ - assets = world[plugin_assets.Assets] - world.new_entity().set(Sprite(assets.get_texture("background"))) - - -SCENE = plugin_assets.loading_scene( - Scene( - [], - [], - [], - ), - "one_player", -)