Animated Sprite¶
frame animation from a spritesheet.
▶ Run in browserTags: 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 atfpsframes 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 setsplayingto 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())