From afcd2a6c96cfa7fb5d698ccc1fcc6118a8f8bb53 Mon Sep 17 00:00:00 2001 From: Jaskrendix Date: Fri, 28 Feb 2025 10:57:01 +0100 Subject: [PATCH] animation --- pyscroll/animation.py | 54 +++++++++++++++++++++++++++++++++---------- pyscroll/data.py | 4 ++-- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/pyscroll/animation.py b/pyscroll/animation.py index 504d1ab..a955cfc 100644 --- a/pyscroll/animation.py +++ b/pyscroll/animation.py @@ -1,19 +1,30 @@ from __future__ import annotations -from collections import namedtuple from collections.abc import Sequence -from typing import Union +from typing import NamedTuple, Union + +from pygame import Surface + + +class AnimationFrame(NamedTuple): + image: Surface + duration: float + -AnimationFrame = namedtuple("AnimationFrame", "image duration") TimeLike = Union[float, int] __all__ = ("AnimationFrame", "AnimationToken") class AnimationToken: - __slots__ = ["next", "positions", "frames", "index"] - - def __init__(self, positions, frames: Sequence, initial_time: int = 0) -> None: + __slots__ = ["_next", "positions", "frames", "index"] + + def __init__( + self, + positions: set[tuple[int, int, int]], + frames: Sequence[AnimationFrame], + initial_time: float = 0.0, + ) -> None: """ Constructor @@ -22,14 +33,19 @@ def __init__(self, positions, frames: Sequence, initial_time: int = 0) -> None: frames: Sequence of frames that compromise the animation initial_time: Used to compensate time between starting and changing animations + Raises: + ValueError: If the frames sequence is empty """ - frames = tuple(AnimationFrame(*i) for i in frames) + if not frames: + raise ValueError("Frames sequence cannot be empty") + + frames = tuple(AnimationFrame(*frame_data) for frame_data in frames) self.positions = positions self.frames = frames - self.next = frames[0].duration + initial_time + self._next = frames[0].duration + initial_time self.index = 0 - def advance(self, last_time: TimeLike): + def advance(self, last_time: TimeLike) -> AnimationFrame: """ Advance the frame, and set timer for next frame @@ -43,6 +59,8 @@ def advance(self, last_time: TimeLike): Args: last_time: Duration of the last frame + Returns: + AnimationFrame: The next frame in the animation """ # advance the animation frame index, looping by default if self.index == len(self.frames) - 1: @@ -52,11 +70,23 @@ def advance(self, last_time: TimeLike): # set the timer for the next advance next_frame = self.frames[self.index] - self.next = next_frame.duration + last_time + self._next = next_frame.duration + last_time return next_frame def __lt__(self, other): + """ + Compare the animation token with another object based on the next frame time + + Args: + other: The object to compare with + + Returns: + bool: True if the next frame time is less than the other object's time + """ try: - return self.next < other.next + return self._next < other._next except AttributeError: - return self.next < other + return self._next < other + + def __repr__(self) -> str: + return f"AnimationToken(positions={self.positions}, frames={self.frames})" diff --git a/pyscroll/data.py b/pyscroll/data.py index 860f031..07e0aed 100644 --- a/pyscroll/data.py +++ b/pyscroll/data.py @@ -72,7 +72,7 @@ def process_animation_queue( # verify that there are tile substitutions ready self._update_time() try: - if self._animation_queue[0].next > self._last_time: + if self._animation_queue[0]._next > self._last_time: return new_tiles # raised with the animation queue is empty (no animations at all) @@ -84,7 +84,7 @@ def process_animation_queue( get_tile_image = self.get_tile_image # test if the next scheduled tile change is ready - while self._animation_queue[0].next <= self._last_time: + while self._animation_queue[0]._next <= self._last_time: # get the next tile/frame which is ready to be changed token = heappop(self._animation_queue)