gtn/src/plugins/animation.py

102 lines
2.9 KiB
Python

"""
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
# 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],
[],
)