simvx.core.animation

Animation system for SimVX.

Provides property animations, sprite sheet animations, timeline-based keyframe playback, and animation state machines. All features are backend-agnostic and work with the core node system.

Module Contents

Classes

TweenChain

Chainable tween builder for sequential animations.

Sprite2D

2D sprite node — renders a texture via Draw2D.draw_texture().

SpriteAnimation

Named sprite animation with frame range.

AnimatedSprite2D

Sprite with frame-based animation from sprite sheets.

AnimationEvent

Event that fires when playback crosses a specific timestamp.

Track

Property animation track with keyframe interpolation.

AnimationClip

Timeline-based animation with multiple property tracks.

AnimationPlayer

Plays timeline-based animation clips on a target node.

AnimationState

Single state in animation state machine.

Transition

Transition between animation states.

AnimationTree

Animation state machine with parameter-based transitions.

BlendSpace1D

Blends between animation clips along a single parameter axis.

BlendSpace2D

Blends between animation clips positioned in 2D parameter space.

BoneTrack

Animation track for a single bone: position, rotation, scale keyframes.

SkeletalAnimationClip

Animation clip with bone tracks for skeletal animation.

Functions

ease_linear

Linear interpolation (no easing).

ease_in_quad

Quadratic ease-in.

ease_out_quad

Quadratic ease-out.

ease_in_out_quad

Quadratic ease-in-out.

ease_in_cubic

Cubic ease-in.

ease_out_cubic

Cubic ease-out.

ease_in_out_cubic

Cubic ease-in-out.

ease_in_quart

Quartic ease-in.

ease_out_quart

Quartic ease-out.

ease_in_out_quart

Quartic ease-in-out.

ease_in_quint

Quintic ease-in.

ease_out_quint

Quintic ease-out.

ease_in_out_quint

Quintic ease-in-out.

ease_in_sine

Sine ease-in.

ease_out_sine

Sine ease-out.

ease_in_out_sine

Sine ease-in-out.

ease_in_expo

Exponential ease-in.

ease_out_expo

Exponential ease-out.

ease_in_out_expo

Exponential ease-in-out.

ease_in_back

Back ease-in (overshoots).

ease_out_back

Back ease-out (overshoots).

ease_in_out_back

Back ease-in-out (overshoots).

ease_in_elastic

Elastic ease-in (spring effect).

ease_out_elastic

Elastic ease-out (spring effect).

ease_in_out_elastic

Elastic ease-in-out (spring effect).

ease_in_bounce

Bounce ease-in.

ease_out_bounce

Bounce ease-out.

ease_in_out_bounce

Bounce ease-in-out.

tween

Enhanced property tween generator with callbacks and repeating.

Data

API

simvx.core.animation.log

‘getLogger(…)’

simvx.core.animation.__all__

[‘tween’, ‘TweenChain’, ‘ease_linear’, ‘ease_in_quad’, ‘ease_out_quad’, ‘ease_in_out_quad’, ‘ease_in…

simvx.core.animation.ease_linear(t: float) float

Linear interpolation (no easing).

simvx.core.animation.ease_in_quad(t: float) float

Quadratic ease-in.

simvx.core.animation.ease_out_quad(t: float) float

Quadratic ease-out.

simvx.core.animation.ease_in_out_quad(t: float) float

Quadratic ease-in-out.

simvx.core.animation.ease_in_cubic(t: float) float

Cubic ease-in.

simvx.core.animation.ease_out_cubic(t: float) float

Cubic ease-out.

simvx.core.animation.ease_in_out_cubic(t: float) float

Cubic ease-in-out.

simvx.core.animation.ease_in_quart(t: float) float

Quartic ease-in.

simvx.core.animation.ease_out_quart(t: float) float

Quartic ease-out.

simvx.core.animation.ease_in_out_quart(t: float) float

Quartic ease-in-out.

simvx.core.animation.ease_in_quint(t: float) float

Quintic ease-in.

simvx.core.animation.ease_out_quint(t: float) float

Quintic ease-out.

simvx.core.animation.ease_in_out_quint(t: float) float

Quintic ease-in-out.

simvx.core.animation.ease_in_sine(t: float) float

Sine ease-in.

simvx.core.animation.ease_out_sine(t: float) float

Sine ease-out.

simvx.core.animation.ease_in_out_sine(t: float) float

Sine ease-in-out.

simvx.core.animation.ease_in_expo(t: float) float

Exponential ease-in.

simvx.core.animation.ease_out_expo(t: float) float

Exponential ease-out.

simvx.core.animation.ease_in_out_expo(t: float) float

Exponential ease-in-out.

simvx.core.animation.ease_in_back(t: float) float

Back ease-in (overshoots).

simvx.core.animation.ease_out_back(t: float) float

Back ease-out (overshoots).

simvx.core.animation.ease_in_out_back(t: float) float

Back ease-in-out (overshoots).

simvx.core.animation.ease_in_elastic(t: float) float

Elastic ease-in (spring effect).

simvx.core.animation.ease_out_elastic(t: float) float

Elastic ease-out (spring effect).

simvx.core.animation.ease_in_out_elastic(t: float) float

Elastic ease-in-out (spring effect).

simvx.core.animation.ease_in_bounce(t: float) float

Bounce ease-in.

simvx.core.animation.ease_out_bounce(t: float) float

Bounce ease-out.

simvx.core.animation.ease_in_out_bounce(t: float) float

Bounce ease-in-out.

class simvx.core.animation.TweenChain(obj, prop: str, start_value=None)

Chainable tween builder for sequential animations.

Example: TweenChain(obj, ‘position’, Vec3(0, 0, 0))
.to(Vec3(10, 0, 0), 1.0, ease_out_quad)
.wait(0.5)
.to(Vec3(0, 0, 0), 1.0, ease_in_quad)
.on_complete(lambda: print(“Done!”))
.build()

Initialization

to(target, duration: float, easing=ease_linear)

Add a tween step.

wait(duration: float, fps: float = 60.0)

Add a wait step.

on_complete(callback: collections.abc.Callable)

Set completion callback.

build(fps: float = 60.0) simvx.core.descriptors.Coroutine

Build the coroutine chain.

Args: fps: Frames per second for duration calculations.

simvx.core.animation.tween(obj, prop: str, target, duration: float, easing=ease_linear, delay: float = 0, repeat: int = 1, on_step: collections.abc.Callable[[float], None] | None = None, on_repeat: collections.abc.Callable[[int], None] | None = None, on_complete: collections.abc.Callable | None = None, fps: float = 60.0) simvx.core.descriptors.Coroutine

Enhanced property tween generator with callbacks and repeating.

Note: Uses frame-based timing (not real-time). Each yield represents one frame. Duration is converted to frame count based on fps parameter.

Args: obj: Object to animate. prop: Property name to animate. target: Target value. duration: Animation duration in seconds. easing: Easing function (default: linear). delay: Delay before starting animation in seconds. repeat: Number of times to repeat (1 = play once, 2 = repeat once, etc). on_step: Called each frame with current progress (0.0 to 1.0). on_repeat: Called when a repeat cycle completes, with repeat index. on_complete: Called when all repeats complete. fps: Frames per second for duration calculation (default: 60).

Example: yield from tween( player, ‘position’, Vec3(10, 0, 0), 2.0, easing=ease_out_quad, delay=0.5, repeat=2, on_complete=lambda: print(“Done!”) )

class simvx.core.animation.Sprite2D(texture: str | None = None, position=None, rotation: float = 0.0, scale=None, color: tuple = (1.0, 1.0, 1.0, 1.0), width: int = 0, height: int = 0, **kwargs)

Bases: simvx.core.engine.Node2D

2D sprite node — renders a texture via Draw2D.draw_texture().

The texture property holds a file path; the graphics backend loads it via TextureManager and stores the GPU index in _texture_id. The draw() callback emits a textured quad through the renderer (Draw2D).

Attributes: texture: Path to the image file (PNG/JPG). color: RGBA tint (0.0-1.0 floats). width: Display width in pixels (0 = use texture native size). height: Display height in pixels (0 = use texture native size).

Initialization

texture

‘Property(…)’

color

‘Property(…)’

width

‘Property(…)’

height

‘Property(…)’

property texture_path: str
draw(renderer) None

Emit a textured quad via the renderer (Draw2D).

to_dict() dict

Serialize sprite state.

classmethod from_dict(data: dict)

Deserialize sprite state.

z_index

‘Property(…)’

z_as_relative

‘Property(…)’

property absolute_z_index: int
property position: simvx.core.math.types.Vec2
property rotation: float
property rotation_degrees: float
property scale: simvx.core.math.types.Vec2
property global_position: simvx.core.math.types.Vec2
property global_rotation: float
property global_scale: simvx.core.math.types.Vec2
property forward: simvx.core.math.types.Vec2
property right: simvx.core.math.types.Vec2
translate(offset: tuple[float, float] | numpy.ndarray)
rotate(radians: float)
rotate_deg(degrees: float)
look_at(target: tuple[float, float] | numpy.ndarray)
transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]
draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, color=None)
wrap_screen(margin: float = 20)
script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
enter_tree() None
exit_tree() None
process(dt: float) None
physics_process(dt: float) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
destroy()
queue_free

None

property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
get_settings

None

__repr__()
class simvx.core.animation.SpriteAnimation

Named sprite animation with frame range.

name: str

None

frames: list[int]

None

fps: float

10.0

loop: bool

True

class simvx.core.animation.AnimatedSprite2D(texture: str = None, frames_horizontal: int = 1, frames_vertical: int = 1, frame_width: int | None = None, frame_height: int | None = None, **kwargs)

Bases: simvx.core.animation.Sprite2D

Sprite with frame-based animation from sprite sheets.

Inherits from Sprite2D (Node2D), so it participates in the scene tree and gets process(dt) and draw(renderer) called automatically.

Example: sprite = AnimatedSprite2D( texture=”player.png”, frames_horizontal=4, frames_vertical=4 ) sprite.add_animation(“walk”, frames=[0, 1, 2, 3], fps=10, loop=True) sprite.add_animation(“jump”, frames=[4, 5, 6], fps=15, loop=False) sprite.play(“walk”)

Initialization

add_animation(name: str, frames: list[int], fps: float = 10.0, loop: bool = True)

Register a named animation.

play(animation_name: str = 'default')

Play named animation.

stop()

Stop animation at current frame.

pause()

Pause animation (alias for stop).

resume()

Resume animation.

process(dt: float)

Advance sprite animation each frame.

draw(renderer) None

Draw the current animation frame as a textured quad with proper UVs.

get_current_frame_index() int

Get absolute frame index in sprite sheet.

get_frame_uv() tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2]

Get UV coordinates for current frame (top-left, bottom-right).

to_dict() dict

Serialize animated sprite.

classmethod from_dict(data: dict)

Deserialize animated sprite.

texture

‘Property(…)’

color

‘Property(…)’

width

‘Property(…)’

height

‘Property(…)’

property texture_path: str
z_index

‘Property(…)’

z_as_relative

‘Property(…)’

property absolute_z_index: int
property position: simvx.core.math.types.Vec2
property rotation: float
property rotation_degrees: float
property scale: simvx.core.math.types.Vec2
property global_position: simvx.core.math.types.Vec2
property global_rotation: float
property global_scale: simvx.core.math.types.Vec2
property forward: simvx.core.math.types.Vec2
property right: simvx.core.math.types.Vec2
translate(offset: tuple[float, float] | numpy.ndarray)
rotate(radians: float)
rotate_deg(degrees: float)
look_at(target: tuple[float, float] | numpy.ndarray)
transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]
draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, color=None)
wrap_screen(margin: float = 20)
script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
enter_tree() None
exit_tree() None
physics_process(dt: float) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
destroy()
queue_free

None

property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
get_settings

None

__repr__()
class simvx.core.animation.AnimationEvent

Event that fires when playback crosses a specific timestamp.

Attributes: time: Timestamp in seconds when the event fires. callback: Function to call when the event triggers. args: Positional arguments passed to callback.

time: float

None

callback: collections.abc.Callable

None

args: tuple

()

class simvx.core.animation.Track(property_name: str)

Property animation track with keyframe interpolation.

Stores (time, value) keyframes and interpolates between them. Optionally holds AnimationEvents that fire when playback crosses their timestamp.

Initialization

add_keyframe(time: float, value: Any)

Add keyframe at given time.

add_event(time: float, callback: collections.abc.Callable, *args)

Add an event that fires when playback crosses the given timestamp.

Args: time: Timestamp in seconds. callback: Function to invoke. *args: Additional arguments forwarded to callback.

fire_events(prev_time: float, cur_time: float) None

Fire events whose timestamps fall in the half-open interval (prev_time, cur_time].

Args: prev_time: Playback time at previous frame. cur_time: Playback time at current frame.

evaluate(time: float) Any

Evaluate track at given time.

class simvx.core.animation.AnimationClip(name: str, duration: float)

Timeline-based animation with multiple property tracks.

Example: clip = AnimationClip(“jump”, duration=1.0) clip.add_track(“position”, [ (0.0, Vec3(0, 0, 0)), (0.5, Vec3(0, 5, 0)), (1.0, Vec3(0, 0, 0)), ]) clip.add_track(“rotation”, [ (0.0, 0.0), (1.0, 360.0), ])

Initialization

add_track(property_name: str, keyframes: list[tuple[float, Any]], easing=ease_linear)

Add property track with keyframes.

evaluate(time: float) dict[str, Any]

Evaluate all tracks at given time.

to_dict() dict

Serialize clip.

classmethod from_dict(data: dict)

Deserialize clip.

class simvx.core.animation.AnimationPlayer(target=None, **kwargs)

Bases: simvx.core.engine.Node

Plays timeline-based animation clips on a target node.

As a Node subclass, it participates in the scene tree and gets process(dt) called automatically. By default it animates its parent.

Supports crossfading between clips and firing track events.

Attributes: target: The node whose properties are animated. Defaults to parent if not set explicitly. clips: Dictionary of registered AnimationClip objects keyed by name. current_clip: Name of the currently playing clip, or None. current_time: Playback position within the current clip (seconds). playing: Whether playback is active. speed_scale: Playback speed multiplier (1.0 = normal). loop: Whether the current clip should loop when finished. animation_finished: Signal emitted when a non-looping clip ends.

Example::

player = AnimationPlayer()
player.add_clip(jump_clip)
player.add_clip(run_clip)
player.play("jump")
player.crossfade("run", duration=0.3)

Initialization

add_clip(clip: simvx.core.animation.AnimationClip)

Register animation clip.

play(clip_name: str, loop: bool = False)

Play animation clip, cancelling any active crossfade.

crossfade(clip_name: str, duration: float = 0.3)

Blend from the current clip to a new clip over duration seconds.

If no clip is playing or the target clip is unknown, falls back to play().

stop()

Stop playback.

pause()

Pause playback (alias for stop).

resume()

Resume playback.

seek(time: float)

Jump to time in current clip.

process(dt: float)

Advance animation playback each frame (called by SceneTree).

to_dict() dict

Serialize player state.

classmethod from_dict(data: dict, target=None)

Deserialize player.

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
enter_tree() None
exit_tree() None
physics_process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
destroy()
queue_free

None

property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
get_settings

None

__repr__()
class simvx.core.animation.AnimationState

Single state in animation state machine.

name: str

None

clip: simvx.core.animation.AnimationClip

None

speed_scale: float

1.0

loop: bool

True

class simvx.core.animation.Transition

Transition between animation states.

from_state: str

None

to_state: str

None

condition: collections.abc.Callable[[], bool]

None

blend_time: float

0.2

class simvx.core.animation.AnimationTree(target=None, **kwargs)

Bases: simvx.core.engine.Node

Animation state machine with parameter-based transitions.

As a Node subclass, it participates in the scene tree and gets process(dt) called automatically. By default it animates its parent.

Example: tree = AnimationTree(target=player) tree.add_state(“idle”, idle_clip, loop=True) tree.add_state(“run”, run_clip, loop=True) tree.add_state(“jump”, jump_clip, loop=False)

tree.add_transition("idle", "run", lambda: tree.parameters["speed"] > 0.1)
tree.add_transition("run", "idle", lambda: tree.parameters["speed"] < 0.1)
tree.add_transition("idle", "jump", lambda: tree.parameters["jump_pressed"])

tree.set_parameter("speed", 0.0)
tree.start("idle")

Initialization

add_state(name: str, clip: simvx.core.animation.AnimationClip, speed_scale: float = 1.0, loop: bool = True)

Add animation state.

add_transition(from_state: str, to_state: str, condition: collections.abc.Callable[[], bool], blend_time: float = 0.2)

Add transition with condition function.

set_parameter(name: str, value: Any)

Update animation parameter.

get_parameter(name: str, default: Any = None) Any

Get animation parameter.

start(state_name: str)

Start animation tree with initial state.

stop()

Stop animation tree.

process(dt: float)

Advance animation state machine each frame (called by SceneTree).

to_dict() dict

Serialize tree state.

classmethod from_dict(data: dict, target=None)

Deserialize tree.

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
enter_tree() None
exit_tree() None
physics_process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
destroy()
queue_free

None

property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
get_settings

None

__repr__()
class simvx.core.animation.BlendSpace1D

Blends between animation clips along a single parameter axis.

Each clip is placed at a numeric position on the axis. When the parameter is set, the two nearest clips are blended proportionally.

Example: bs = BlendSpace1D() bs.add_point(idle_clip, 0.0) bs.add_point(walk_clip, 0.5) bs.add_point(run_clip, 1.0)

bs.set_parameter(0.75)           # blend walk+run
value = bs.sample("speed", 0.5)  # sample at t=0.5 in blended clips

Initialization

add_point(clip: simvx.core.animation.AnimationClip, position: float) None

Register a clip at a position on the blend axis.

set_parameter(value: float) None

Set the current blend parameter value.

property parameter: float
sample(property_path: str, time: float) Any

Sample a property at time with the current blend parameter.

Returns the blended value by evaluating the two nearest clips.

sample_all(time: float) dict[str, Any]

Sample all properties at time using the current blend parameter.

class simvx.core.animation.BlendSpace2D

Blends between animation clips positioned in 2D parameter space.

Uses inverse-distance weighted interpolation across all points for robustness (falls back to exact match when the parameter lands directly on a point).

Example: bs = BlendSpace2D() bs.add_point(idle_clip, (0.0, 0.0)) bs.add_point(walk_fwd_clip, (0.0, 1.0)) bs.add_point(strafe_r_clip, (1.0, 0.0))

bs.set_parameter(0.5, 0.5)
value = bs.sample("position", 0.5)

Initialization

add_point(clip: simvx.core.animation.AnimationClip, position: tuple[float, float]) None

Register a clip at a 2D position.

set_parameter(x: float, y: float) None

Set the current 2D blend parameter.

sample(property_path: str, time: float) Any

Sample a single property with the current 2D blend parameter.

sample_all(time: float) dict[str, Any]

Sample all properties with the current 2D blend parameter.

class simvx.core.animation.BoneTrack(bone_index: int)

Animation track for a single bone: position, rotation, scale keyframes.

Rotation keyframes use quaternions [x, y, z, w] for spherical interpolation.

Initialization

sample(time: float) numpy.ndarray

Interpolate keyframes at given time → 4x4 local transform matrix.

class simvx.core.animation.SkeletalAnimationClip(name: str, duration: float)

Animation clip with bone tracks for skeletal animation.

Initialization

add_bone_track(track: simvx.core.animation.BoneTrack) None
evaluate(time: float) dict[int, numpy.ndarray]

Evaluate all bone tracks at given time.

Returns dict mapping bone_index → 4x4 local transform.