diff --git a/assets/sounds/click.wav b/assets/sounds/click.wav new file mode 100644 index 0000000..2797a50 Binary files /dev/null and b/assets/sounds/click.wav differ diff --git a/src/engine.py b/src/engine.py index 3bac300..0c61496 100644 --- a/src/engine.py +++ b/src/engine.py @@ -283,6 +283,7 @@ class Assets: # Cache des ressources self.__textures: dict[str, pygame.Surface] = {} self.__fonts: dict[int, pygame.font.Font] = {} + self.__sounds: dict[str, pygame.mixer.Sound] = {} def get_texture(self, name: str) -> pygame.Surface: """ @@ -346,6 +347,22 @@ class Assets: """ return Vec2(*self.get_font(size).size(text)) + def get_sound(self, name: str) -> pygame.mixer.Sound: + """ + Renvoie le son qui correspond au nom *name*. + + Paramètres: + name: Le nom du son. + + Retourne: + Le son qui correspond au nom *name*. + """ + sound = self.__sounds.get(name) + if sound is None: + sound = pygame.mixer.Sound(f"assets/sounds/{name}") + self.__sounds[name] = sound + return sound + class Time(float): """ @@ -561,6 +578,26 @@ class Clickable: self.callback = callback +class Sound: + """ + Composant qui une entité emettrant un son. + """ + + def __init__( + self, + name: str, + volume: float = 0.5, + loop: bool = False, + callback: Callable[[World, Entity], object] = lambda _w, _e: None, + stop_on_remove: bool = False, + ) -> None: + self.name = name + self.volume = volume + self.loop = loop + self.callback = callback + self.stop_on_remove = stop_on_remove + + class Scene: """ Une scène dans le jeu. @@ -606,6 +643,9 @@ def start_game( # Chargements des assets assets = Assets(surface) + # creation des channels pour les sons + channels: dict[Entity, tuple[bool, pygame.mixer.Channel]] = {} + # On récupère la première scène scene = scenes.get(start_scene) @@ -701,6 +741,35 @@ def start_game( for system in scene.update_systems: system(world) + # Gestion des sons en cours + sound_entities = world.query(Sound) + entities_to_delete: list[Entity] = [] + for entity, (stop_on_remove, channel) in channels.items(): + if Sound in entity: + entity_sound = entity[Sound] + channel.set_volume(entity_sound.volume) + if not channel.get_busy(): + entities_to_delete.append(entity) + del entity[Sound] + entity_sound.callback(world, entity) + continue + if stop_on_remove and entity not in sound_entities: + entities_to_delete.append(entity) + channel.stop() + for entity in entities_to_delete: + del channels[entity] + + # Ajout des sons non gérés + for entity in sound_entities: + if entity not in channels: + entity_sound = entity[Sound] + sound = assets.get_sound(entity_sound.name) + channel = sound.play(loops=-1 if entity_sound.loop else 0) + if channel is None: # type: ignore + continue + channel.set_volume(entity_sound.volume) + channels[entity] = entity_sound.stop_on_remove, channel + # Mise à jour des animations for entity in world.query(Animation): animation = entity[Animation] diff --git a/src/scenes/menu.py b/src/scenes/menu.py index 124f5c9..0f60cd4 100644 --- a/src/scenes/menu.py +++ b/src/scenes/menu.py @@ -6,11 +6,13 @@ from engine import ( Centered, Clickable, Display, + Entity, Game, HoveredTexture, Order, Position, Scene, + Sound, Texture, World, ) @@ -26,10 +28,18 @@ def __create_button(world: World, i: int, name: str): Centered(), Texture(f"menu/button_{name}.png"), HoveredTexture(f"menu/button_{name}_hover.png"), - Clickable(lambda world, _: world[Game].change_scene(name)), + 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. + """ + entity[Sound] = Sound("click.wav") + world[Game].change_scene(name) + + def __initialize_world(world: World): """ Initialise le monde du menu.