Animated Sprite

frame animation from a spritesheet.

▶ Run in browser

Tags: 2d sprite animation spritesheet

AnimatedSprite2D is a Sprite2D that shows one cell of a sheet at a time. Give it the sheet as texture and say how the sheet is cut with frames_h (columns) and frames_v (rows); cells are numbered left to right, then top to bottom, starting at 0.

  • add_animation(name, frames, fps, loop) registers a named sequence of cell indices played at fps frames per second.

  • play(name) starts an animation from its first frame. Calling it again on the same animation restarts it, which is how a one-shot is replayed.

  • A looping animation wraps back to its first frame; a one-shot (loop=False) stops on its last frame and sets playing to False.

The sheet here is built in code as an RGBA uint8 NumPy array, an 8-cell strip of a dot sweeping round a ring, so the example needs no image file; a file path or PNG bytes work the same way. The three sprites share that one sheet: a loop at 10 fps, the same loop at 24 fps, and a one-shot at 12 fps. Press Space, click or tap to replay the one-shot.

Source

 1# /// simvx
 2# tags = ["sprite", "animation", "spritesheet"]
 3# web = { width = 800, height = 600, responsive = true }
 4# ///
 5"""Animated Sprite: frame animation from a spritesheet.
 6
 7`AnimatedSprite2D` is a `Sprite2D` that shows one cell of a sheet at a time. Give it the sheet as
 8`texture` and say how the sheet is cut with `frames_h` (columns) and `frames_v` (rows); cells are
 9numbered left to right, then top to bottom, starting at 0.
10
11- `add_animation(name, frames, fps, loop)` registers a named sequence of cell indices played at
12  `fps` frames per second.
13- `play(name)` starts an animation from its first frame. Calling it again on the same animation
14  restarts it, which is how a one-shot is replayed.
15- A looping animation wraps back to its first frame; a one-shot (`loop=False`) stops on its last
16  frame and sets `playing` to False.
17
18The sheet here is built in code as an RGBA `uint8` NumPy array, an 8-cell strip of a dot sweeping
19round a ring, so the example needs no image file; a file path or PNG bytes work the same way. The
20three sprites share that one sheet: a loop at 10 fps, the same loop at 24 fps, and a one-shot at
2112 fps. Press Space, click or tap to replay the one-shot.
22"""
23
24import numpy as np
25
26from simvx.core import Anchor, AnimatedSprite2D, CanvasLayer, Key, Label, Node2D, Pointer, Text2D, on_input
27from simvx.graphics import App
28
29CELL = 64  # pixels per sheet cell
30CELLS = 8  # cells in the horizontal strip
31
32
33def make_spritesheet() -> np.ndarray:
34    """An 8-cell strip: a dot one step further round a ring in each cell."""
35    sheet = np.zeros((CELL, CELL * CELLS, 4), dtype=np.uint8)
36    yy, xx = np.ogrid[0:CELL, 0:CELL]
37    for i in range(CELLS):
38        cell = sheet[:, i * CELL : (i + 1) * CELL]
39        cell[:] = (24, 24, 36, 255)
40        cell[[0, 1, -2, -1]] = (60, 60, 90, 255)  # top and bottom border, so the cells read as distinct
41        angle = i / CELLS * 2 * np.pi
42        mx, my = CELL / 2 + np.cos(angle) * CELL * 0.32, CELL / 2 + np.sin(angle) * CELL * 0.32
43        cell[(xx - mx) ** 2 + (yy - my) ** 2 <= 7**2] = (255, 210, 70, 255)
44    return sheet
45
46
47class AnimatedSpriteScene(Node2D):
48    input_context = {"replay": [Key.SPACE, Pointer.PRIMARY]}
49
50    def on_ready(self):
51        sheet = make_spritesheet()
52        every_cell = list(range(CELLS))
53        self.sprites = []
54        for name, fps, loop, caption in [
55            ("Loop", 10, True, "10 fps loop"),
56            ("Fast", 24, True, "24 fps loop"),
57            ("Once", 12, False, "one-shot"),
58        ]:
59            sprite = self.add_child(AnimatedSprite2D(name=name, texture=sheet, frames_h=CELLS, width=128, height=128))
60            sprite.add_animation("spin", frames=every_cell, fps=fps, loop=loop)
61            sprite.play("spin")
62            sprite.add_child(Text2D(text=caption, align="centre", position=(0, 80)))
63            self.sprites.append(sprite)
64        self.once = self.sprites[-1]
65
66        hud = self.add_child(CanvasLayer())
67        hud.add_child(Label("AnimatedSprite2D", font_size=24, anchor=Anchor.TOP_LEFT, inset=10))
68        hud.add_child(Label("Space, click or tap: replay the one-shot", anchor=Anchor.BOTTOM_LEFT, inset=10))
69
70    def on_update(self, dt):
71        width, height = self.viewport_size
72        for i, sprite in enumerate(self.sprites):
73            sprite.position = (width * (i + 1) / 4, height / 2)
74
75    @on_input("replay")
76    def replay(self, _event):
77        self.once.play("spin")
78
79
80if __name__ == "__main__":
81    App(title="AnimatedSprite2D", width=800, height=600).run(AnimatedSpriteScene())