"""
Utility class for loading spritesheets.
"""
import os
from . import Animation
from .... import Vector, get_path, Surface
[docs]class Spritesheet:
"""
A spritesheet from the filesystem.
Args:
path: The relative path to the spritesheet.
sprite_size: The size of each sprite in the spritesheet. Defaults to (32, 32).
grid_size: The size of the grid of sprites in the spritesheet. Set to None to automatically determine the
grid size. Defaults to None.
Raises:
IndexError: If user does not load the entire sheet.
"""
def __init__(
self,
path: str,
sprite_size: Vector | tuple[float, float] = (32, 32),
grid_size: Vector | tuple[float, float] | None = None
):
self._sprite_size: tuple[int, int] = (int(sprite_size[0]), int(sprite_size[1]))
self._sheet = Surface.from_file(path)
self._sprites: list[list[Surface]] = []
if not grid_size:
self._grid: tuple[int, int] = (self._sheet.size_scaled() / self._sprite_size).tuple_int()
else:
self._grid: tuple[int, int] = (int(grid_size[0]), int(grid_size[1]))
if Vector(*self._sprite_size) * self._grid != self._sheet.size_scaled():
raise IndexError("Sprite and grid size do not match given spritesheet size.")
for y in range(0, self._grid[1] * self._sprite_size[1], self._sprite_size[1]):
self._sprites.append([])
for x in range(0, self._grid[0] * self._sprite_size[0], self._sprite_size[0]):
surface = Surface(*self._sprite_size)
surface._blit(
self._sheet,
(x, y, self._sprite_size[0], self._sprite_size[1]),
)
self._sprites[y // self._sprite_size[1]].append(surface)
@property
def grid_size(self) -> Vector:
"""The size of the spritesheet grid (readonly)."""
return Vector(*self._grid)
@property
def sprite_size(self) -> Vector:
"""The size of each sprite (readonly)."""
return Vector(*self._sprite_size)
@property
def sheet(self) -> Surface:
"""The actual spritesheet sprite (readonly)."""
return self._sheet
@property
def sprites(self) -> list[list[Surface]]:
"""The list of all the sprites as sprites (readonly)."""
return self._sprites
@property
def end(self):
"""
The end indexes of the Spritesheet as a vector.
Example:
You can use :code:`spritesheet.get(*spritesheet.end)` to get the final Sprite
"""
return self.grid_size - Vector.one()
[docs] def get(self, x: int, y: int) -> Surface:
"""
Gets the Sprite at the corresponding grid coordinate of the spritesheet.
Args:
x: The x grid coordinate.
y: The y grid coordinate.
Raises:
IndexError: One or both of the coordinates are out of range of the spritesheet's size.
Returns:
The Sprite at the corresponding coordinate.
"""
if x >= self.grid_size.x or y >= self.grid_size.y:
raise IndexError(f"The coordinates ({x}, {y}) are out of range of the spritesheet.")
return self.sprites[y][x].clone()
[docs] @staticmethod
def from_folder(
path: str,
sprite_size: Vector | tuple[float, float],
default_state: str | None = None,
recursive: bool = True
) -> Animation:
"""
Creates an Animation from a folder of spritesheets.
Directory must be comprised solely of spritesheets.
Added alphabetically. Default is the first sheet loaded.
Args:
path: The relative path to the folder you wish to import
sprite_size: The size of a single sprite in your spritesheet, should be the same in all imported sheets.
default_state: Sets the default state of the animation.
recursive: Whether it will import an animation shallowly or recursively. Defaults to True.
Returns:
Animation: the animation loaded from the folder of spritesheets
"""
anim = Animation()
path = get_path(path)
if not recursive:
_, _, files = next(os.walk(path))
# walk to directory path and ignore name and subdirectories
files.sort()
for sprite_path in files:
path_to_spritesheet = os.path.join(path, sprite_path)
sprite_sheet = Spritesheet(
path=path_to_spritesheet,
sprite_size=sprite_size,
)
anim.add_spritesheet(sprite_path.split(".")[0], sprite_sheet, to_coord=sprite_sheet.end)
else:
for _, _, files in os.walk(path):
# walk to directory path and ignore name and subdirectories
files.sort()
for sprite_path in files:
path_to_spritesheet = os.path.join(path, sprite_path)
sprite_sheet = Spritesheet(
path=path_to_spritesheet,
sprite_size=sprite_size,
)
anim.add_spritesheet(sprite_path.split(".")[0], sprite_sheet, to_coord=sprite_sheet.end)
if default_state:
anim.default_state = default_state
anim.current_state = default_state
anim.reset()
return anim