"""
This file contains the implementation of the class Animation: steps
through a sequence of frames at a fixed time interval, with an
optional loop count.
Author: Alejandro Mujica (aledrums@gmail.com)
"""
from typing import Sequence, Optional, Any, Callable
[docs]
class Animation:
"""
This class represents animations as a sequence of frames. Those
frames change in a given time interval.
"""
def __init__(
self,
frames: Sequence[Any],
time_interval: float = 0,
loops: Optional[int] = None,
on_finish: Optional[Callable[[], None]] = None,
) -> None:
"""
Initialize a new Animation.
:param frames: Sequence of frames
:param time_interval: Duration time (in seconds) of each frame.
:param loops: Number of times that this animation shall execute. The default value is None to execute infinitely.
:param on_finish: Callback to do something after finish loops. The default value is an empty lambda.
"""
self.frames: Sequence[Any] = frames
self.interval: float = time_interval
self.loops: Optional[int] = loops
self.size: int = len(self.frames)
self.timer: float = 0
self.times_played: int = 0
self.current_frame_index: int = 0
self.on_finish: Callable[[], None] = (
(lambda: None) if on_finish is None else on_finish
)
[docs]
def reset(self) -> None:
"""
Set the animation on its initial values.
"""
self.times_played = 0
self.timer = 0
self.current_frame_index = 0
[docs]
def update(self, dt: float) -> None:
"""
This function updates the animation timer to check whether the frame
should be changed or not. If the animation has only one frame or
it has executed the number of times defined by loops, then
this operation does not execute.
"""
if self.size <= 1 or (
self.loops is not None and self.times_played >= self.loops
):
return
self.timer += dt
if self.timer >= self.interval:
self.timer %= self.interval
self.current_frame_index = (self.current_frame_index + 1) % self.size
# Only increments times played if there is a value for loops.
if self.current_frame_index == 0 and self.loops is not None:
self.times_played += 1
if self.times_played >= self.loops:
# Setting the last frame if loops was completed
self.current_frame_index = len(self.frames) - 1
# Animation fulfilled invoking callback, if exist
self.on_finish()
[docs]
def get_current_frame(self) -> Any:
"""
:returns: The current frame of the animation.
"""
return self.frames[self.current_frame_index]