Skip to content

Animation

Sprite animations and their player, animation assets, Animator keyframe clips, tweens, easing curves, state machines and skeletal animation.

Files: animation.odin, animation_player.odin, animation_sets.odin, animator.odin, tween.odin, easing.odin, state_machine.odin, skeleton.odin, dragonbones.odin.

Animation assets: a sheet and named animations that AnimatedSprite nodes share, so one set of animations (a hero’s idle, run, jump) is made once and used anywhere.

  • Files: an .anim.xml file (asset kind “Animation”: ParseAnimationSet, its format there), or an Aseprite file, read as one (AnimationSet_FromAseprite): its frames laid out on a sheet (Aseprite_SheetPixels; the texture is the file’s path plus ASEPRITE_SHEET_SUFFIX, which the resource manager turns into that sheet), one animation per tag (its frames, each frame’s duration, its direction, its repeats; a tag that doesn’t say how often loops, as in Aseprite), or, without tags, one “default” animation of every frame.
  • Scene files: an AnimatedSprite’s animations="assets/hero.aseprite" plays the set instead of its own sheet and animations; its autoplay (when set) picks the first one.
  • Code: AnimatedSprite_UseAnimations(sprite, ANIMATION_HERO) switches a sprite to a set.
  • Game_LoadAnimationSet reads each file once (Game.animationSets, in the game’s arena); editors fill that map with their in-memory sets before building, as they do components.

Animator nodes: keyframe animation of any node’s properties (Godot’s AnimationPlayer). An Animator holds clips (“intro”, “open”, “hit”); a clip has a length and tracks; a track animates one property of one node through keys (a time and a value). Between two keys the value moves along the first key’s easing curve (EaseType), or holds until the next key on a step track. Uses: cutscenes, doors and platforms that move, UI that slides in, a hit flash, a coin’s bob and spin.

<node kind="Animator" name="Anim" autoplay="bob">
<clip name="bob" length="1" loop="true">
<track node="Coin" property="position.y">
<key time="0" value="0" ease="InOutSine"/>
<key time="0.5" value="-6" ease="InOutSine"/>
<key time="1" value="0"/>
</track>
<track property="event"><key time="0.5" value="top"/></track>
</clip>
</node>
  • Which node: a track’s node is a path from the Animator’s parent (Node_FindPath from there: “Coin” is a sibling, “” or “.” the parent itself, “Coin/Shine” a child of it, “/World” from the top level). Put the Animator inside what it animates.
  • Properties (ANIMATOR_PROPERTIES): position (a UI control’s offset from its anchor), position.x, position.y, rotation, scale, scale.x, scale.y, tint, opacity (the tint’s alpha, 0 to 1), visible, zIndex; an AnimatedSprite’s animation (plays it by name); a Label’s text and color; a script’s exported field, script.<field> (numbers, vectors and colors move, the rest step). Values are text as in scene files: "12", "10, -4", "#ff8000", "true".
  • Events: a track with the property event (its node unused) fires the Animator’s onEvent with the key’s value as each of its keys passes: listen to it in the inspector to call a script’s procedure at that moment (a footstep sound, a camera shake, a spawned enemy).
  • Playing: autoplay starts a clip when the scene starts; from code Animator_Play, Animator_Stop, Animator_Pause, Animator_Seek, speed. A clip that doesn’t loop stops at its end and fires onFinished (data: its name). The Animator follows its process mode like any node (a paused scene pauses it).
  • Order: an Animator sets values in its update, so a target updated after it (its own script) can still change them that frame.
  • Clips can be made from code too: Animator_SetClips with AnimatorSettings data.

State machines: a StateMachine node keeps one of a set of states (idle, run, jump, attack, hurt) and moves between them by transitions whose conditions read parameters your scripts set (a speed, whether it’s on the ground, a “jump” trigger). Each state can play an animation of an AnimatedSprite, so a character’s animation follows what it’s doing without code picking animations.

<node kind="StateMachine" name="Brain" start="idle">
<param name="speed" type="Float"/>
<param name="grounded" type="Bool" value="true"/>
<param name="jump" type="Trigger"/>
<state name="idle" animation="idle"/>
<state name="run" animation="run"/>
<state name="jump" animation="jump"/>
<transition from="idle" to="run" when="speed > 10"/>
<transition from="run" to="idle" when="speed <= 10"/>
<transition from="*" to="jump" when="jump"/>
<transition from="jump" to="idle" when="grounded" after="0.2"/>
</node>
  • Parameters (StateMachineParam): Float (a number), Bool (true or false) and Trigger (set, then cleared when a transition uses it). Scripts set them: StateMachine_SetFloat, StateMachine_SetBool, StateMachine_SetTrigger.
  • Transitions (StateMachineTransition): from a state (or *: from any other state) to another, when its condition holds: clauses joined by and, each name (a Bool that’s true, a Trigger that’s set), !name, or name <op> number with ==, !=, <, <=, >, >= (Bools compare as 1 and 0). after: not before that many seconds in the state; onFinished: only once the state’s animation ended (a non-looping one). They’re checked in order every update; the first that holds is taken (at most one per update).
  • Animations: a state’s animation plays (from its start) on the target AnimatedSprite (a path from the StateMachine: “” its parent) when the state is entered.
  • Events: onStateEntered and onStateExited (data: the state’s name). Listen to them to do things on entering a state (a sound when attacking, damage when hurt).
  • Code: StateMachine_Current, StateMachine_Is, StateMachine_TimeInState, StateMachine_Travel (go to a state now).

Skeletal animation: bones, skinned meshes and inverse kinematics.

  • Bone2D nodes are bones: a node like any other (position, rotation, scale relative to its parent, so a bone’s children follow it), pointing along its local x axis for length pixels. A tree of them under a Skeleton2D node is a character’s skeleton. Sprites under a bone move with it rigidly (a cut-out character); the Animator and the Timeline key bones’ rotation, position and scale like any node’s.
  • SkinnedMesh nodes bend one picture over several bones: a mesh of triangles over the picture (MeshData: vertices in the node’s space, the picture’s pixels under them, up to four weighted bones per vertex). The pose the scene file has is the rest pose: when the mesh is built, each bone’s place relative to the mesh is remembered (SkinnedMesh_Bind); every draw moves each vertex by its bones’ moves since, blended by their weights (linear blend skinning), and draws the triangles through the node’s transform.
  • IKChain nodes aim a chain of bones at a target (inverse kinematics: an arm reaching for a point, a leg planted on the ground): tip is the last bone of the chain, length how many bones up from it move, target the node to reach. Two bones are solved exactly (the elbow or knee bending the bendPositive way); longer chains with cyclic coordinate descent. Chains are solved after the scene’s nodes and tweens update (the Animator’s keys first, then IK), in the order they were made.

Scene files (format 13):

<node kind="Skeleton2D" name="Hero">
<node kind="Bone2D" name="Hip" boneLength="40">
<node kind="Bone2D" name="Thigh" rotation="90" boneLength="50"/>
</node>
<node kind="SkinnedMesh" name="Body" texture="assets/hero.png">
<mesh vertices="0,0 10,0 ..." uvs="0,0 10,0 ..." triangles="0,1,2 ..."
bones="../Hip,../Hip/Thigh" weights="0:1;0:0.5 1:0.5;..."/>
</node>
<node kind="IKChain" name="LegIK" ikTip="../Hip/Thigh" ikLength="2" ikTarget="../Foot"/>
</node>

DragonBones files are converted into these nodes (dragonbones.odin).

DragonBones files (dragonbones.github.io, DragonBones Pro’s JSON export, format 5.x) converted into the engine’s own nodes (skeleton.odin), so a character made there plays like one made in the editor:

  • the armature (<name>_ske.json’s first one) becomes a Skeleton2D, its bones Bone2D nodes (position, rotation, scale and length as set up);
  • each slot’s display becomes a Sprite on its bone (an image) or a SkinnedMesh (a mesh: its vertices, picture coordinates, triangles and bone weights), drawn in the slots’ order (zIndex);
  • the texture atlas (<name>_tex.json and its picture) becomes an atlas (atlas.odin) the sprites and meshes draw regions of;
  • the animations become an Animator’s clips: each bone’s translate, rotate and scale frames as position, rotation and scale tracks (the setup pose plus the frame’s offset), looping when they play forever.

DragonBones_Convert does it in memory; editors write the result as a component and an atlas asset (the Crossfire Editor’s import of a _ske.json).

Not converted (each reported in the warnings): slots switching displays or colors over time, FFD (mesh vertex) animations, skins other than the first, nested armatures, bounding boxes, IK constraints (add IKChain nodes), skewed bones (only rotation), rotated atlas regions, and curve easings (they play linearly; tweenEasing null steps).

AnimationPlayMode :: enum {
// startFrame -> endFrame
Forward,
// endFrame -> startFrame
Reverse,
// startFrame -> endFrame -> startFrame. One full there-and-back counts as one loop
PingPong,
// endFrame -> startFrame -> endFrame (Aseprite's "Ping-pong Reverse")
PingPongReverse,
}

How an animation steps through its frames.

enum. Source: animation.odin:11

Animation :: struct {
// Name the AnimationPlayer plays it by (owned, allocated with `allocator`)
name: string,
// First source frame played, inclusive
startFrame: int,
// Last source frame played, inclusive
endFrame: int,
// Frames per second. 0 or less holds the current frame
fps: f32,
// Restart after the last frame instead of ending
loop: bool,
// Times it plays before it ends, when it doesn't loop (0 or 1: once; Aseprite's tag repeats)
repeat: int,
// How it steps through its frames
playMode: AnimationPlayMode,
// Seconds each frame shows (relative frames: 0 is startFrame), overriding `fps` for the frames
// it covers (Aseprite frames each have their own duration). Owned, with `allocator`
durations: []f32,
// Each frame's events, comma-separated ("" for none), sent by its player's `onFrameEvent` when
// the frame shows (relative frames; owned, with `allocator`)
events: []string,
// Number of frames this animation plays: endFrame - startFrame + 1
totalFrames: proc(animation: ^Animation) -> int,
// Number of frames available in the source (sheet cells, textures)
sourceFrameCount: proc(animation: ^Animation) -> int,
// Native pixel size of a source frame
frameSize: proc(animation: ^Animation, frame: int) -> [2]f32,
// Draws a source frame into dest (world coordinates, camera aware)
drawFrame: proc(animation: ^Animation, renderer: ^sdl.Renderer, frame: int, dest: sdl.FRect, rotation: f32, tint: sdl.Color, flip: sdl.FlipMode),
// Allocator the animation and its data were created with; used to free them
allocator: mem.Allocator,
// Frees subtype data. Called by Animation_Destroy
destroyData: proc(animation: ^Animation),
}

Base type of every animation. Specific animation types embed it with using:

MyAnimation :: struct {
using animation: Animation,
... // the frames' source data
}

and fill in the procedure fields, which is how the AnimationPlayer draws any animation type without knowing what it is. Use the Create* procedures rather than building one by hand.

Frames are numbered by the animation’s source (sprite sheet cell or texture index). startFrame..endFrame (both inclusive) selects which of them this animation plays, so one sprite sheet can hold several animations.

struct. Source: animation.odin:37

SpriteSheetAnimation :: struct {
// The animation part: name, frame range, fps, procedures
using animation: Animation,
// The sprite sheet (not owned: the resource manager's, usually)
texture: ^sdl.Texture,
// Number of cells across
columns: int,
// Number of cells down
rows: int,
// Pixels around the outside of the grid
margin: f32,
// Pixels between neighboring cells
spacing: f32,
}

An animation whose frames are cells of one texture, laid out on a grid.

Cells are numbered left to right, then top to bottom, starting at 0:

+---+---+---+
| 0 | 1 | 2 |
+---+---+---+
| 3 | 4 | 5 |
+---+---+---+

struct. Source: animation.odin:175

FrameAnimation :: struct {
// The animation part: name, frame range, fps, procedures
using animation: Animation,
// The frame textures, in order (the slice is owned; the textures aren't)
frames: []^sdl.Texture,
}

An animation where every frame is a separate texture. Frames are numbered by their position in frames, starting at 0.

struct. Source: animation.odin:316

AnimationPlayer :: struct {
// The animations it owns, by the index `AnimationPlayer_Add` returned
animations: [dynamic]^Animation,
// Animation name -> index in `animations`
names: map[string]int,
// Index of the current animation, -1 if none
current: int,
// Index of the animation that was current before the last change, -1 if none
previous: int,
// Current frame, relative to the current animation's startFrame
frame: int,
// True while frames are advancing
playing: bool,
// True after a non-looping animation reached its end
finished: bool,
// Playback speed multiplier. 1 is normal, 2 double speed, 0 frozen
speed: f32,
// Seconds spent on the current frame
elapsed: f32,
// +1 or -1: direction frames are currently moving (PingPong flips it)
direction: int,
// Times the current animation has played through (for its `repeat`)
cycles: int,
// data: int, the new relative frame. Also sent when an animation starts on its first frame
onFrameChanged: EventSubject `event:"The animation's frame changed (data: the frame)" data:"frame := data.(int)"`,
// data: int, index of the new animation (the old one is in `previous`)
onAnimationChanged: EventSubject `event:"Another animation plays (data: its index)" data:"index := data.(int)"`,
// data: int, index of the animation that started (or restarted)
onAnimationStarted: EventSubject `event:"An animation started or restarted (data: its index)" data:"index := data.(int)"`,
// data: int, index of the animation. Sent when a non-looping animation finishes
onAnimationEnded: EventSubject `event:"A non-looping animation finished (data: its index)" data:"index := data.(int)"`,
// data: int, index of the animation. Sent each time a looping animation wraps around
onAnimationLooped: EventSubject `event:"A looping animation wrapped around (data: its index)" data:"index := data.(int)"`,
// data: int, index of the animation. Sent by AnimationPlayer_Stop
onAnimationStopped: EventSubject `event:"The animation was stopped (data: its index)" data:"index := data.(int)"`,
// data: string, an event of the frame that shows (one notification per event; Aseprite cels'
// text or `event` property: `Animation.events`). Valid during the notification
onFrameEvent: EventSubject `event:"A frame with an event shows (data: the event's name)" data:"name := data.(string)"`,
// Allocator of its lists and event listeners (`AnimationPlayer_Init`)
allocator: mem.Allocator,
}

Holds a set of animations, plays one at a time and draws its current frame.

The player is a plain struct, not a Node: embed it in the node that uses it, call AnimationPlayer_Update from the node’s update and AnimationPlayer_Draw from its draw.

Animations are referenced by name or by the index AnimationPlayer_Add returned. Frame numbers reported by the player (events, frame) are relative to the playing animation: 0 is its startFrame, totalFrames() - 1 its endFrame.

Events: subscribe with player.onFrameChanged->subscribe(callback, userData). Every event’s source is the ^AnimationPlayer; data is described on each event below.

Example

Enemy :: struct {
using node: e.Node,
pos: [2]f32,
anim: e.AnimationPlayer,
}
CreateEnemy :: proc(scene: ^e.Scene) -> ^Enemy {
enemy := e.CreateNode(scene, Enemy)
enemy.update = Enemy_update
enemy.draw = Enemy_draw
rm := &scene.game.resourceManager
allocator := e.Node_GetAllocator(enemy) // Animations and listeners are freed with the enemy
e.AnimationPlayer_Init(&enemy.anim, allocator)
sheet := rm->loadTexture("enemy.png") // 6x1 grid
e.AnimationPlayer_Add(&enemy.anim, e.CreateSpriteSheetAnimation("idle", sheet, 6, 1, fps = 6, loop = true, endFrame = 3, allocator = allocator))
e.AnimationPlayer_Add(&enemy.anim, e.CreateSpriteSheetAnimation("die", sheet, 6, 1, fps = 10, startFrame = 4, allocator = allocator))
// Remove the enemy once the death animation finishes. Its memory, including this
// player, its animations and listeners, is freed at the start of the next update
enemy.anim.onAnimationEnded->subscribe(proc(event: ^e.EventSubject, source: rawptr, data: e.EventData, userData: rawptr) {
player := cast(^e.AnimationPlayer)source
if e.AnimationPlayer_CurrentName(player) == "die" {
enemy := cast(^Enemy)userData
enemy.scene->removeNode(enemy.id)
}
}, enemy)
e.AnimationPlayer_Play(&enemy.anim, "idle")
return enemy
}
Enemy_update :: proc(node: ^e.Node, events: []sdl.Event, deltaTime: f64) {
enemy := cast(^Enemy)node
e.AnimationPlayer_Update(&enemy.anim, deltaTime)
}
Enemy_draw :: proc(node: ^e.Node, renderer: ^sdl.Renderer) {
enemy := cast(^Enemy)node
size := e.AnimationPlayer_FrameSize(&enemy.anim)
e.AnimationPlayer_Draw(&enemy.anim, renderer, {enemy.pos.x, enemy.pos.y, size.x, size.y})
}

struct. Source: animation_player.odin:66

AnimatorValueKind :: enum {
// A number: moves smoothly
Float,
// Two numbers ("x, y"): move smoothly
Vector,
// A color (`#rrggbb` or `#rrggbbaa`): moves smoothly, channel by channel
Color,
// true or false: steps
Bool,
// Text (an animation's name, a label's text, an enum's value): steps
Text,
}

The kind of value a property holds, which says how keys move between each other

enum. Source: animator.odin:52

AnimatorProperty :: struct {
// Its name in scene files ("position.x")
name: string,
// The value it holds
kind: AnimatorValueKind,
// What tools show ("Position X")
label: string,
// The node type it needs (nil: any node). A type embedding it counts (`typeEmbeds`)
type: typeid,
}

A property tracks can animate (ANIMATOR_PROPERTIES)

struct. Source: animator.odin:66

AnimatorKeyData :: struct {
// Seconds from the clip's start
time: f32,
// The value, as text ("12", "10, -4", "#ff8000", "true", an event's name)
value: string,
// The curve from this key to the next (Linear when absent)
ease: EaseType,
}

A key in a scene file or in code (AnimatorTrackData.keys)

struct. Source: animator.odin:112

AnimatorTrackData :: struct {
// The animated node: a path from the Animator's parent ("" for the parent; unused by events)
node: string,
// The property (`ANIMATOR_PROPERTIES`, "script.<field>" or "event")
property: string,
// Values hold until the next key instead of moving (Bool and Text values always do)
step: bool,
// Its keys, in time order (`AnimatorSettings_Sort` sorts them)
keys: [dynamic]AnimatorKeyData,
}

A track in a scene file or in code (AnimatorClipData.tracks)

struct. Source: animator.odin:122

AnimatorClipData :: struct {
// Its name (`Animator_Play`)
name: string,
// Seconds; keys past it never play
length: f32,
// Starts again at its end (else it stops there and fires `onFinished`)
loop: bool,
// Its tracks
tracks: [dynamic]AnimatorTrackData,
}

A clip in a scene file or in code (AnimatorSettings.clips)

struct. Source: animator.odin:134

AnimatorSettings :: struct {
// Its clips
clips: [dynamic]AnimatorClipData,
// The clip it plays when the scene starts ("": none)
autoplay: string,
// How fast clips play (1: as made; 0 holds them)
speed: f32,
}

An Animator’s settings from a scene file (SceneNodeData.animator): its clips, the one it starts with, its speed. Owned data: free it with AnimatorSettings_Destroy.

struct. Source: animator.odin:149

AnimatorKey :: struct {
// Seconds from the clip's start
time: f32,
// The value (the track's kind)
value: AnimatorValue,
// The curve to the next key
ease: EaseType,
}

A key ready to play: its value parsed

struct. Source: animator.odin:162

AnimatorValue :: union {
f32,
[2]f32,
sdl.Color,
bool,
string,
}

A parsed key value

union. Source: animator.odin:172

AnimatorTrack :: struct {
// The path from the Animator's parent
path: string,
// The property
property: string,
// The value's kind
kind: AnimatorValueKind,
// Values hold between keys
step: bool,
// The data's `step` (Bool and Text values step whatever it says)
stepSet: bool,
// Fires `onEvent` instead of setting a value
event: bool,
// Its keys in time order
keys: []AnimatorKey,
// The node found for the path, and its id (checked before each use: it may be gone)
target: ^Node,
targetId: EntityId,
// The key whose value was set last by a stepping track (-2: none yet), so text and bools are
// set only when they change
applied: int,
}

A track ready to play (in the Animator’s arena)

struct. Source: animator.odin:181

AnimatorClip :: struct {
// Its name
name: string,
// Seconds
length: f32,
// Starts again at its end
loop: bool,
// Its tracks
tracks: []AnimatorTrack,
}

A clip ready to play (in the Animator’s arena)

struct. Source: animator.odin:205

Animator :: struct {
// The node part: transform, hierarchy, hooks
using node: Node,
// Its clips (node arena)
clips: []AnimatorClip,
// The clip playing or paused (-1: none)
current: int,
// Seconds into the current clip
time: f32,
// The clip is moving (false: stopped or paused)
playing: bool,
// How fast clips play (1: as made; 0 holds)
speed: f32,
// The clip to start when the scene starts ("": none; node arena)
autoplay: string,
// Whether autoplay has run
started: bool,
// A clip that doesn't loop reached its end
onFinished: EventSubject `event:"A clip that doesn't loop reached its end (data: its name)" data:"clip := data.(string)"`,
// An event key passed
onEvent: EventSubject `event:"An event key of the playing clip passed (data: the key's value)" data:"name := data.(string)"`,
}

A node playing keyframe animations of other nodes’ properties: see the file’s overview. Made by scene files (kind “Animator”) or CreateAnimator.

struct. Source: animator.odin:220

Tween :: struct {
// The node it belongs to (nil: the scene's own, `Scene_CreateTween`)
owner: ^Node,
// The scene whose update runs it
scene: ^Scene,
// Its steps, in order (heap-allocated, freed with the tween)
steps: [dynamic]TweenStep,
// Index of the first step of the group running now
current: int,
// Seconds into the group running now
elapsed: f32,
// Times to play it again after the first (-1: forever)
loops: int,
// Multiplies its time (1 normal)
speed: f32,
// Set by Tween_Pause; it stays where it is
paused: bool,
// Finished or killed: freed at the next update
done: bool,
// The next step added runs together with the last one
parallelNext: bool,
}

Tweens: values animated over time, step by step, with easing (easing.odin). A tween belongs to a node: it runs while that node may process (Node_CanProcess: pausing stops it), at the game’s time scale, and is killed when the node is removed, so it never writes into freed memory. Animate the node’s own fields (or anything that lives as long as it does).

Steps run one after another; Tween_Parallel makes the next step run together with the previous one. A value step starts from wherever the value is when the step begins (Tween_FromTo for a set start).

Tweens are freed when they finish or are killed: making one per hit or per pickup is fine.

Example

// Given: coin: ^e.Node
// A coin pops up and fades out, then goes
tween := e.Tween_Create(coin)
e.Tween_To(tween, &coin.position, coin.position + {0, -24}, 0.4, .OutBack)
e.Tween_Parallel(tween)
e.Tween_To(tween, &coin.tint, sdl.Color{255, 255, 255, 0}, 0.4)
e.Tween_Call(tween, proc(user: rawptr) {
node := cast(^e.Node)user
e.Scene_RemoveNode(node.scene, node.id)
}, coin)

struct. Source: tween.odin:30

TweenValue :: union {
^f32,
^[2]f32,
^sdl.Color,
}

What a tween step changes

union. Source: tween.odin:54

TweenStep :: struct {
// The value it animates; nil for a delay or a callback
target: TweenValue,
// Its start and end (as up to 4 floats: f32, [2]f32 or a color's channels)
from, to: [4]f32,
// The start was given (Tween_FromTo); otherwise it's read when the step begins
fromGiven: bool,
// It has begun in the current play (reset each loop)
started: bool,
// Seconds it lasts (0 for a callback)
duration: f32,
// The curve from `from` to `to` (easing.odin)
ease: EaseType,
// Called when the step runs (a Tween_Call step)
callback: proc(user: rawptr),
// Passed to `callback`
user: rawptr,
// Runs together with the step before it
parallel: bool,
}

One step of a tween

struct. Source: tween.odin:61

EaseType :: enum {
Linear,
InQuad,
OutQuad,
InOutQuad,
InCubic,
OutCubic,
InOutCubic,
InSine,
OutSine,
InOutSine,
InExpo,
OutExpo,
InOutExpo,
InBack,
OutBack,
InOutBack,
OutElastic,
OutBounce,
}

Easing curves for animating a value over a fixed duration.

“In” curves start slow, “Out” curves end slow, “InOut” do both. Back curves overshoot slightly past the ends, Elastic springs past the end and settles, Bounce bounces at the end. See https://easings.net for a visual reference of each curve. Used by Ease, tweens and camera pans.

enum. Source: easing.odin:13

StateMachineParamKind :: enum {
Float,
Bool,
// Set by code, cleared when a transition uses it
Trigger,
}

What a parameter holds

enum. Source: state_machine.odin:46

StateMachineParam :: struct {
name: string,
kind: StateMachineParamKind,
// Its starting value as text ("0", "true")
value: string,
}

A parameter in a scene file (StateMachineSettings.params)

struct. Source: state_machine.odin:54

StateMachineState :: struct {
name: string,
// The target AnimatedSprite's animation it plays ("": none)
animation: string,
// Where editors draw it in the graph
position: [2]f32,
}

A state in a scene file

struct. Source: state_machine.odin:62

StateMachineTransition :: struct {
// The state it leaves ("*": any other state) and the one it enters
from, to: string,
// Its condition ("": always), e.g. "speed > 10 and grounded"
condition: string,
// Seconds in the state before it can be taken (0: at once)
after: f32,
// Only once the state's animation ended
onFinished: bool,
}

A transition in a scene file

struct. Source: state_machine.odin:71

StateMachineSettings :: struct {
// The state it starts in ("": the first)
start: string,
// The AnimatedSprite its states animate: a path from it ("": its parent)
target: string,
params: [dynamic]StateMachineParam,
states: [dynamic]StateMachineState,
transitions: [dynamic]StateMachineTransition,
}

A state machine’s settings (SceneNodeData.stateMachine). Owned: StateMachineSettings_Destroy.

struct. Source: state_machine.odin:85

StateMachine :: struct {
// The node part: transform, hierarchy, hooks
using node: Node,
// Its states' names and animations (node arena)
states: []StateMachineState,
// Parameters' names, kinds and values (node arena)
paramNames: []string,
paramKinds: []StateMachineParamKind,
values: []f32,
transitions: []RuntimeTransition,
// The current state (-1: before the first update)
current: int,
// Seconds in it
timeInState: f32,
// Its animation ended
animationFinished: bool,
// The start state's index, and the target's path (node arena)
startIndex: int,
targetPath: string,
// The target AnimatedSprite, once found
target: ^AnimatedSprite,
targetId: EntityId,
onStateEntered: EventSubject `event:"It entered a state (data: the state's name)" data:"state := data.(string)"`,
onStateExited: EventSubject `event:"It left a state (data: the state's name)" data:"state := data.(string)"`,
}

A node moving between states: see the file’s overview

struct. Source: state_machine.odin:114

Bone2D :: struct {
using node: Node,
// How long it is, in its own pixels (where its tip is: IK reaches with it; editors draw it)
length: f32,
}

A bone: a node pointing along its x axis

struct. Source: skeleton.odin:46

Skeleton2D :: struct {
using node: Node,
}

The root of a skeleton: bones (and what they carry) are its descendants

struct. Source: skeleton.odin:53

VertexWeights :: struct {
bones: [4]i32,
weights: [4]f32,
}

One vertex’s bones (indices into MeshData.bones, -1 unused) and weights (summing to 1)

struct. Source: skeleton.odin:58

MeshData :: struct {
// Each vertex in the node's space (the rest pose)
vertices: [dynamic][2]f32,
// Each vertex's place in the picture, in pixels (of the atlas region, when the texture is one)
uvs: [dynamic][2]f32,
// Three vertex indices per triangle
triangles: [dynamic]i32,
// The bones, as paths from the mesh node ("../Hip/Thigh")
bones: [dynamic]string,
// Each vertex's bones; none (or fewer than the vertices): those vertices don't move
weights: [dynamic]VertexWeights,
}

A skinned mesh’s triangles over its picture and their bones (scene files’ <mesh>)

struct. Source: skeleton.odin:64

SkinnedMesh :: struct {
using node: Node,
// The picture (and its part: an atlas region; zero for the whole)
texture: ^sdl.Texture,
region: sdl.FRect,
mesh: MeshData,
// Its bones found (`SkinnedMesh_Bind`): their ids, and where each was relative to the mesh then
// (inverted)
boneIds: [dynamic]EntityId,
bind: [dynamic]Transform2D,
bound: bool,
}

A picture bent over bones

struct. Source: skeleton.odin:78

IKSettings :: struct {
// Bones up from the tip that move (1: only the tip turns toward the target)
length: int,
// Two bones: which way the middle joint bends (true: clockwise, like a right arm's elbow)
bendPositive: bool,
// Whether the chain aims its bones (false leaves them as animated)
enabled: bool,
}

An IK chain’s settings (scene files’ ikLength, ikBend, ikEnabled)

struct. Source: skeleton.odin:92

IKChain :: struct {
using node: Node,
settings: IKSettings,
// Paths from the IK node: the chain's last bone, and the node it reaches for (node arena)
tip: string,
target: string,
}

Aims a chain of bones at a target

struct. Source: skeleton.odin:106

DragonBonesResult :: struct {
// A component: the Skeleton2D root and everything under it (free with SceneData_Destroy)
component: SceneData,
// The atlas its sprites and meshes draw from (free with AtlasData_Destroy)
atlas: AtlasData,
// What wasn't converted (temp allocated)
warnings: []string,
}

What DragonBones_Convert makes

struct. Source: dragonbones.odin:35

Animation_TotalFrames :: proc(animation: ^Animation) -> int

Number of frames an animation plays.

Inputs

  • animation: Any animation

Returns

  • endFrame - startFrame + 1

procedure. Source: animation.odin:83

Animation_FrameDuration :: proc(animation: ^Animation, frame: int) -> f32

How long a frame of an animation shows: its duration when the animation has one for it, else 1 / fps.

Inputs

  • animation: Any animation
  • frame: A frame relative to its startFrame

Returns

  • Seconds (0: it holds, for an fps of 0 or less without a duration)

procedure. Source: animation.odin:98

Animation_SetDurations :: proc(animation: ^Animation, durations: []f32)

Sets how long each frame shows, overriding fps for those frames.

Inputs

  • animation: Any animation
  • durations: Seconds per frame, relative to its startFrame (copied; empty: back to fps)

Example

// Given: animation: ^e.Animation
e.Animation_SetDurations(animation, {0.1, 0.1, 0.3})

procedure. Source: animation.odin:114

Animation_SetEvents :: proc(animation: ^Animation, events: []string)

Sets each frame’s events, which the player sends with onFrameEvent as the frame shows.

Inputs

  • animation: Any animation
  • events: Per frame (relative to its startFrame), comma-separated names (“” for none); copied

Example

// Given: animation: ^e.Animation
e.Animation_SetEvents(animation, {"", "step", "", "step,dust"})

procedure. Source: animation.odin:134

Animation_Destroy :: proc(animation: ^Animation)

Frees an animation created by one of the Create* procedures. Animations added to an AnimationPlayer are freed by AnimationPlayer_Destroy; don’t free them yourself.

Inputs

  • animation: The animation to free

procedure. Source: animation.odin:150

CreateSpriteSheetAnimation :: proc(
name: string,
texture: ^sdl.Texture,
columns, rows: int,
fps: f32,
loop := false,
startFrame := 0,
endFrame := -1,
playMode := AnimationPlayMode.Forward,
margin: f32 = 0,
spacing: f32 = 0,
allocator := context.allocator,
) -> ^SpriteSheetAnimation

Creates an animation from a sprite sheet (texture atlas) divided into a grid.

Inputs

  • name: Name used to play the animation from an AnimationPlayer
  • texture: The sprite sheet
  • columns: Number of cells across
  • rows: Number of cells down
  • fps: Frames per second
  • loop: Restart after the last frame instead of ending
  • startFrame: First cell played, inclusive
  • endFrame: Last cell played, inclusive. -1 means the last cell of the sheet
  • playMode: Forward, Reverse or PingPong
  • margin: Pixels around the outside of the grid
  • spacing: Pixels between neighboring cells
  • allocator: Allocator for the animation. Use the owning node’s (Node_GetAllocator) to free it with the node

Returns

Example

// A 4x2 sheet: walk is the top row, jump the bottom row
rm := &game.resourceManager
sheet := rm->loadTexture("hero.png")
walk := e.CreateSpriteSheetAnimation("walk", sheet, 4, 2, fps = 10, loop = true, startFrame = 0, endFrame = 3)
jump := e.CreateSpriteSheetAnimation("jump", sheet, 4, 2, fps = 12, startFrame = 4, endFrame = 7)

procedure. Source: animation.odin:217

SpriteSheetAnimation_FrameRect :: proc(animation: ^SpriteSheetAnimation, frame: int) -> sdl.FRect

Pixel rectangle of a cell in a sprite sheet. Useful for drawing a single cell yourself.

Inputs

  • animation: The sprite sheet animation
  • frame: Cell index in the sheet

Returns

  • The cell’s rectangle in texture pixels

procedure. Source: animation.odin:253

SpriteSheet_CellRect :: proc(sheetSize: [2]f32, columns, rows: int, margin, spacing: f32, frame: int) -> sdl.FRect

A cell’s rectangle in a sprite sheet of a given size, without an animation (editors use it for thumbnails). Cells are numbered left to right, top to bottom.

Inputs

  • sheetSize: The texture’s size in pixels
  • columns: Number of cells across
  • rows: Number of cells down
  • margin: Pixels around the grid
  • spacing: Pixels between cells
  • frame: The cell

Returns

  • The cell’s rectangle in texture pixels

procedure. Source: animation.odin:280

CreateFrameAnimation :: proc(
name: string,
frames: []^sdl.Texture,
fps: f32,
loop := false,
startFrame := 0,
endFrame := -1,
playMode := AnimationPlayMode.Forward,
allocator := context.allocator,
) -> ^FrameAnimation

Creates an animation from a list of textures, one per frame.

Inputs

  • name: Name used to play the animation from an AnimationPlayer
  • frames: The frame textures, in order. The list is copied
  • fps: Frames per second
  • loop: Restart after the last frame instead of ending
  • startFrame: First frame played, inclusive
  • endFrame: Last frame played, inclusive. -1 means the last texture
  • playMode: Forward, Reverse or PingPong
  • allocator: Allocator for the animation. Use the owning node’s (Node_GetAllocator) to free it with the node

Returns

Example

rm := &game.resourceManager
blink := e.CreateFrameAnimation("blink", {rm->loadTexture("eye_open.png"), rm->loadTexture("eye_closed.png")}, fps = 4, loop = true)

procedure. Source: animation.odin:343

CreateFrameAnimationFromFiles :: proc(
rm: ^ResourceManager,
name: string,
paths: []string,
fps: f32,
loop := false,
startFrame := 0,
endFrame := -1,
playMode := AnimationPlayMode.Forward,
allocator := context.allocator,
) -> ^FrameAnimation

Creates a FrameAnimation by loading each frame through the resource manager.

Inputs

  • rm: Resource manager to load (and cache) the textures with
  • name: Name used to play the animation from an AnimationPlayer
  • paths: Image file paths, one per frame, in order
  • fps: Frames per second
  • loop: Restart after the last frame instead of ending
  • startFrame: First frame played, inclusive
  • endFrame: Last frame played, inclusive. -1 means the last texture
  • playMode: Forward, Reverse or PingPong
  • allocator: Allocator for the animation. Use the owning node’s (Node_GetAllocator) to free it with the node

Returns

  • The new animation

Example

rm := &game.resourceManager
light := e.CreateFrameAnimationFromFiles(rm, "blink", {"light_off.png", "light_on.png"}, fps = 2, loop = true)

procedure. Source: animation.odin:385

AnimationPlayer_Init :: proc(player: ^AnimationPlayer, allocator := context.allocator)

Prepares a player for use.

Inputs

  • player: The player to initialize
  • allocator: Allocator for the player’s lists and event listeners. Use the owning node’s allocator (Node_GetAllocator) so they are freed with the node

procedure. Source: animation_player.odin:120

AnimationPlayer_Destroy :: proc(player: ^AnimationPlayer)

Frees the player’s animations, lists and event listeners.

Not needed when the player and its animations use their node’s allocator (Node_GetAllocator): everything, including event listeners, is freed with the node.

Inputs

  • player: The player to free

procedure. Source: animation_player.odin:146

AnimationPlayer_Add :: proc(player: ^AnimationPlayer, animation: ^Animation) -> int

Adds an animation to the player, which takes ownership of it.

Inputs

  • player: The player
  • animation: Any animation type (SpriteSheetAnimation, FrameAnimation, …)

Returns

  • The animation’s index, usable instead of its name. -1 (logged) if the name is already taken: the player then doesn’t own it, so free it yourself unless it uses a node’s allocator

Example

// Given: player: ^e.AnimationPlayer, sheet: ^sdl.Texture
e.AnimationPlayer_Add(player, e.CreateSpriteSheetAnimation("run", sheet, 8, 1, fps = 12, loop = true))

procedure. Source: animation_player.odin:176

AnimationPlayer_Play :: proc{AnimationPlayer_PlayByName, AnimationPlayer_PlayByIndex}

Starts playing an animation by name or index.

If that animation is already playing it keeps going unless restart is true, so it is safe to call every frame (e.g. Play("run") while a movement key is held).

Sends onAnimationChanged (if it differs from the current one), onAnimationStarted and onFrameChanged for its first frame.

Inputs

Returns

  • false if no such animation exists

Example

// Given: player: ^e.AnimationPlayer
e.AnimationPlayer_Play(player, "thrust")
e.AnimationPlayer_Play(player, 0, restart = true)

procedure group. Source: animation_player.odin:210

AnimationPlayer_PlayByName :: proc(player: ^AnimationPlayer, name: string, restart := false) -> bool

AnimationPlayer_Play by the animation’s name

procedure. Source: animation_player.odin:213

AnimationPlayer_PlayByIndex :: proc(player: ^AnimationPlayer, index: int, restart := false) -> bool

AnimationPlayer_Play by the index AnimationPlayer_Add returned

procedure. Source: animation_player.odin:223

AnimationPlayer_Clear :: proc(player: ^AnimationPlayer)

Removes every animation (freeing them) and stops: nothing plays until animations are added again.

Inputs

  • player: The player

procedure. Source: animation_player.odin:263

AnimationPlayer_Pause :: proc(player: ^AnimationPlayer)

Pauses playback on the current frame. Resume with AnimationPlayer_Resume.

Inputs

  • player: The player

procedure. Source: animation_player.odin:283

AnimationPlayer_Resume :: proc(player: ^AnimationPlayer)

Continues a paused animation from its current frame. Does nothing if the animation finished.

Inputs

  • player: The player

procedure. Source: animation_player.odin:293

AnimationPlayer_Stop :: proc(player: ^AnimationPlayer)

Stops playback and rewinds to the first frame of the current animation. Sends onAnimationStopped.

Inputs

  • player: The player

procedure. Source: animation_player.odin:303

AnimationPlayer_SetFrame :: proc(player: ^AnimationPlayer, frame: int)

Jumps to a frame of the current animation without changing whether it is playing.

Inputs

  • player: The player
  • frame: Frame relative to the animation’s startFrame. Clamped to the animation’s frames

procedure. Source: animation_player.odin:327

AnimationPlayer_Update :: proc(player: ^AnimationPlayer, deltaTime: f64)

Advances the current animation. Call once per frame from the owning node’s update.

Inputs

  • player: The player
  • deltaTime: Seconds since the last frame

Example

// Given: player: ^e.AnimationPlayer
e.AnimationPlayer_Update(player, deltaTime)

procedure. Source: animation_player.odin:348

AnimationPlayer_Draw :: proc(
player: ^AnimationPlayer,
renderer: ^sdl.Renderer,
dest: sdl.FRect,
rotation: f32 = 0,
tint := sdl.Color{255, 255, 255, 255},
flip := sdl.FlipMode.NONE,
)

Draws the current frame of the current animation. Camera aware, like DrawTexture.

Inputs

  • player: The player
  • renderer: The renderer to draw with
  • dest: Where to draw, in world coordinates. Use AnimationPlayer_FrameSize for the native size
  • rotation: Degrees, clockwise around the center of dest
  • tint: Multiplies the frame’s colors and alpha
  • flip: Mirror horizontally and/or vertically, e.g. to face left

Example

// Given: player: ^e.AnimationPlayer
size := e.AnimationPlayer_FrameSize(player)
e.AnimationPlayer_Draw(player, renderer, {100, 100, size.x * 2, size.y * 2}, flip = .HORIZONTAL)

procedure. Source: animation_player.odin:384

AnimationPlayer_FrameSize :: proc(player: ^AnimationPlayer) -> [2]f32

Native pixel size of the frame currently showing.

Inputs

  • player: The player

Returns

  • The frame’s width and height, or {0, 0} if nothing is playing

procedure. Source: animation_player.odin:406

AnimationPlayer_Get :: proc{AnimationPlayer_GetByName, AnimationPlayer_GetByIndex}

Looks up an animation by name or index.

Inputs

Returns

  • The animation, or nil if there is none. Cast to the subtype to change type-specific settings

procedure group. Source: animation_player.odin:423

AnimationPlayer_GetByName :: proc(player: ^AnimationPlayer, name: string) -> ^Animation

AnimationPlayer_Get by name

procedure. Source: animation_player.odin:426

AnimationPlayer_GetByIndex :: proc(player: ^AnimationPlayer, index: int) -> ^Animation

AnimationPlayer_Get by index

procedure. Source: animation_player.odin:432

AnimationPlayer_IndexOf :: proc(player: ^AnimationPlayer, name: string) -> (int, bool)

Index of an animation by name.

Inputs

  • player: The player
  • name: The animation’s name

Returns

  • The index and true, or -1 and false if there is no such animation

procedure. Source: animation_player.odin:447

AnimationPlayer_Current :: proc(player: ^AnimationPlayer) -> ^Animation

The current animation.

Inputs

  • player: The player

Returns

  • The animation, or nil if none has been played

procedure. Source: animation_player.odin:461

AnimationPlayer_CurrentName :: proc(player: ^AnimationPlayer) -> string

Name of the current animation.

Inputs

  • player: The player

Returns

  • The name (the animation’s string), or “” if none has been played

procedure. Source: animation_player.odin:474

AnimationPlayer_IsPlaying :: proc(player: ^AnimationPlayer, name: string) -> bool

Whether the named animation is the current one and is playing (not paused, stopped or finished).

Inputs

  • player: The player
  • name: The animation’s name

Returns

  • true if it is playing

procedure. Source: animation_player.odin:489

AnimationSet_FromAseprite :: proc(ase: ^Aseprite, path: string, allocator := context.allocator) -> AnimationSetData

An Aseprite file as an animation set: every frame on a sheet, an animation per tag.

Inputs

  • ase: The file
  • path: Its path, relative to the project folder (the sheet’s texture is path + ASEPRITE_SHEET_SUFFIX)
  • allocator: For the set’s strings and lists

Returns

procedure. Source: animation_sets.odin:38

Game_LoadAnimationSet :: proc(game: ^Game, path: string) -> (^AnimationSetData, bool)

An animation asset’s set, read the first time and kept for the game’s life, in the game’s arena (sprites share it). A file that can’t be read is remembered too: it isn’t read again.

Inputs

  • game: The game
  • path: The .anim.xml or Aseprite file, relative to the project folder (Game.projectDir)

Returns

  • The set (don’t free it), and false (logged) if it can’t be read

procedure. Source: animation_sets.odin:94

AnimatedSprite_SetAnimations :: proc(sprite: ^AnimatedSprite, set: ^AnimationSetData, projectDir: string, autoplay := "") -> bool

Makes a sprite play a set of animations: its animations become the set’s (its old ones go), on the set’s sheet, and it plays autoplay (else the set’s own, else the first).

Inputs

  • sprite: The AnimatedSprite
  • set: The animations (Game_LoadAnimationSet)
  • projectDir: The folder the set’s texture path is relative to
  • autoplay: The animation to play (“”: the set’s choice)

Returns

  • false (logged) if the set’s sheet can’t be loaded

procedure. Source: animation_sets.odin:131

AnimatedSprite_UseAnimations :: proc(sprite: ^AnimatedSprite, asset: AnimationAsset, autoplay := "") -> bool

Switches an AnimatedSprite to an animation asset (an .anim.xml or Aseprite file), from a script.

Inputs

  • sprite: The sprite
  • asset: The animation asset (ANIMATION_X in scripts/assets.odin)
  • autoplay: The animation to play (“”: the asset’s choice)

Returns

  • false (logged) if the asset can’t be read

Example

e.AnimationAsset
e.AnimatedSprite_UseAnimations(sprite, ANIMATION_HERO_ARMORED, "idle")

procedure. Source: animation_sets.odin:170

Asset_Animations :: proc(game: ^Game, asset: AnimationAsset) -> ^AnimationSetData

An animation asset’s set (read once, kept by the game).

Inputs

  • game: The game
  • asset: The animation asset

Returns

  • The set (the game’s), or nil (logged) if it can’t be read

procedure. Source: animation_sets.odin:188

CreateAnimator :: proc(scene: ^Scene) -> ^Animator

Creates an Animator node with no clips and adds it to the scene (at the start of the next update). Give it clips with Animator_SetClips.

Inputs

  • scene: The scene that owns it

Returns

  • The Animator

Example

anim := e.CreateAnimator(scene)
e.Node_AddChild(node, anim)

procedure. Source: animator.odin:257

Animator_SetClips :: proc(animator: ^Animator, settings: AnimatorSettings)

Replaces an Animator’s clips with copies of these (parsed into its arena), and its autoplay clip and speed. A playing clip stops.

Inputs

  • animator: The Animator
  • settings: The clips (copied: the caller keeps them), autoplay and speed

Example

// Given: anim: ^e.Animator
// Turns its parent once a second (the data is copied: temporary lists will do)
track := e.AnimatorTrackData{property = "rotation", keys = make([dynamic]e.AnimatorKeyData, context.temp_allocator)}
append(&track.keys, e.AnimatorKeyData{time = 0, value = "0"}, e.AnimatorKeyData{time = 1, value = "360"})
clip := e.AnimatorClipData{name = "spin", length = 1, loop = true, tracks = make([dynamic]e.AnimatorTrackData, context.temp_allocator)}
append(&clip.tracks, track)
settings := e.AnimatorSettings{speed = 1, clips = make([dynamic]e.AnimatorClipData, context.temp_allocator)}
append(&settings.clips, clip)
e.Animator_SetClips(anim, settings)
e.Animator_Play(anim, "spin")

procedure. Source: animator.odin:287

Animator_Play :: proc(animator: ^Animator, name: string, restart := false) -> bool

Plays a clip from its start. The clip already playing goes on, and a paused one resumes, unless restart. Event keys at time 0 fire.

Inputs

  • animator: The Animator
  • name: The clip’s name
  • restart: Start again even when it’s the clip playing

Returns

  • false when it has no clip of that name

Example

// Given: anim: ^e.Animator
e.Animator_Play(anim, "open")

procedure. Source: animator.odin:349

Animator_Stop :: proc(animator: ^Animator, reset := false)

Stops the current clip. Its values stay as they are, unless reset, which sets them to the clip’s start.

Inputs

  • animator: The Animator
  • reset: Put the clip’s values back to its first keys

Example

// Given: anim: ^e.Animator
e.Animator_Stop(anim, reset = true)

procedure. Source: animator.odin:382

Animator_Pause :: proc(animator: ^Animator, paused: bool)

Pauses or resumes the current clip where it is.

Inputs

  • animator: The Animator
  • paused: true pauses, false resumes

Example

// Given: anim: ^e.Animator
e.Animator_Pause(anim, true)

procedure. Source: animator.odin:399

Animator_Seek :: proc(animator: ^Animator, time: f32)

Jumps the current clip to a time and sets its values there (no events fire).

Inputs

  • animator: The Animator
  • time: Seconds from the clip’s start (clamped to its length)

Example

// Given: anim: ^e.Animator
e.Animator_Seek(anim, 0.5)

procedure. Source: animator.odin:415

Animator_IsPlaying :: proc(animator: ^Animator) -> bool

Whether a clip is playing (not stopped, paused or finished).

Inputs

  • animator: The Animator

Returns

  • true while one plays

procedure. Source: animator.odin:431

Animator_CurrentClip :: proc(animator: ^Animator) -> string

The current clip’s name (playing, paused or finished; “” when none has played).

Inputs

  • animator: The Animator

Returns

  • Its name (the Animator’s)

procedure. Source: animator.odin:444

Animator_FindClip :: proc(animator: ^Animator, name: string) -> int

Finds a clip by name.

Inputs

  • animator: The Animator
  • name: The clip’s name

Returns

  • Its index in animator.clips, or -1

procedure. Source: animator.odin:458

Animator_Apply :: proc(animator: ^Animator, clip: int, time: f32)

Sets every track of a clip to its value at a time, without playing it or firing events: what an editor’s timeline shows while scrubbing.

Inputs

  • animator: The Animator
  • clip: The clip’s index
  • time: Seconds from its start

procedure. Source: animator.odin:472

Animator_PropertyKind :: proc(property: string) -> (AnimatorValueKind, bool)

The kind of a built-in property’s value (ANIMATOR_PROPERTIES).

Inputs

  • property: The property’s name

Returns

  • Its kind, and false when it isn’t a built-in one (a script field, an event, or unknown)

procedure. Source: animator.odin:487

Animator_TypeHasProperty :: proc(type: typeid, property: AnimatorProperty) -> bool

Whether a node type has a property (a built-in one needing a type, like animation).

Inputs

  • type: The node’s type (node.type, or a kind’s SCENE_NODE_TYPES)
  • property: The property

Returns

  • true when nodes of the type have it

procedure. Source: animator.odin:502

Animator_ScriptFieldKind :: proc(type: ScriptFieldType) -> (AnimatorValueKind, bool)

How a script field is animated: numbers, vectors and colors move, booleans step, and text, enums and asset handles step as text. Node fields can’t be animated.

Inputs

  • type: The field’s type

Returns

  • Its kind, and false for node fields

procedure. Source: animator.odin:516

Animator_ParseValue :: proc(kind: AnimatorValueKind, text: string, allocator := context.allocator) -> (AnimatorValue, bool)

Reads a key’s value of a kind from text.

Inputs

  • kind: The value’s kind
  • text: The text (“12”, “10, -4”, “#ff8000”, “true”)
  • allocator: For text values

Returns

  • The value, and false when the text isn’t one

procedure. Source: animator.odin:538

Animator_FormatValue :: proc(value: AnimatorValue, allocator := context.allocator) -> string

Writes a value as key text (what Animator_ParseValue reads).

Inputs

  • value: The value
  • allocator: For the text

Returns

  • Its text

procedure. Source: animator.odin:574

Animator_ReadProperty :: proc(node: ^Node, property: string, allocator := context.allocator) -> (string, bool)

A node’s current value of a property, as key text: what an editor records as a key. Script fields need the node’s script.

Inputs

  • node: The node
  • property: The property (ANIMATOR_PROPERTIES or “script.<field>”)
  • allocator: For the text

Returns

  • The text, and false when the node has no such property

procedure. Source: animator.odin:597

Node_WriteProperty :: proc(node: ^Node, property: string, text: string) -> bool

Sets a node’s property from text: what tools that edit a running game use (the editor’s live view). The properties are the Animator’s (ANIMATOR_PROPERTIES the node’s type has, and “script.<field>” for its script’s exported fields).

Inputs

  • node: The node
  • property: The property (“position”, “script.speed”)
  • text: The value as text (“10, 20”, “#ff8000”, “true”)

Returns

  • false when the node has no such property or the text isn’t a value of it

Example

// Given: hero: ^e.Node
e.Node_WriteProperty(hero, "position", "100, 200")

procedure. Source: animator.odin:620

Node_Properties :: proc(node: ^Node, allocator := context.allocator) -> []string

The properties a node has (as Node_WriteProperty and Animator_ReadProperty take them): the built-in ones its type has, then its script’s fields (node fields left out).

Inputs

  • node: The node
  • allocator: For the list

Returns

  • The property names

procedure. Source: animator.odin:647

AnimatorSettings_Sort :: proc(settings: ^AnimatorSettings)

Sorts every track’s keys by time (stable): call it after changing keys’ times.

Inputs

  • settings: The settings

procedure. Source: animator.odin:673

AnimatorSettings_Clone :: proc(settings: AnimatorSettings, allocator := context.allocator) -> AnimatorSettings

A deep copy of Animator settings.

Inputs

  • settings: The settings to copy
  • allocator: For the copy

Returns

procedure. Source: animator.odin:692

AnimatorSettings_Destroy :: proc(settings: ^AnimatorSettings)

Frees Animator settings (made by the parser or AnimatorSettings_Clone) and empties them.

Inputs

  • settings: The settings

procedure. Source: animator.odin:715

AnimatorSettings_Equal :: proc(a, b: AnimatorSettings) -> bool

Whether two Animator settings are the same: every clip, track and key.

Inputs

  • a, b: The settings

Returns

  • true when equal

procedure. Source: animator.odin:740

AnimatorTrack_ValueAt :: proc(track: ^AnimatorTrack, time: f32) -> (AnimatorValue, int)

The value a track has at a time: the keys around it blended along the first one’s curve (or the key before it on stepping tracks).

Inputs

  • track: The track (its keys in time order)
  • time: Seconds from the clip’s start

Returns

  • The value, and the index of the key at or before the time (-1 before the first: its value then)

procedure. Source: animator.odin:765

Tween_Create :: proc(owner: ^Node) -> ^Tween

Makes a tween owned by a node (in the node’s scene). Add steps to it right away; it starts on the scene’s next update.

Inputs

  • owner: The node it belongs to: it runs while the node may process, and dies with it

Returns

  • The tween (freed when it finishes or is killed; don’t keep the pointer past that)

Example

tween := e.Tween_Create(node)
e.Tween_To(tween, &node.scale, [2]f32{1.2, 1.2}, 0.15, .OutQuad)
e.Tween_To(tween, &node.scale, [2]f32{1, 1}, 0.15, .InQuad)

procedure. Source: tween.odin:97

Scene_CreateTween :: proc(scene: ^Scene) -> ^Tween

Makes a tween that belongs to the scene itself (no node): it runs while the scene isn’t paused, and dies with the scene. Only animate values that live as long as the scene.

Inputs

  • scene: The scene

Returns

  • The tween (freed when it finishes or is killed; don’t keep the pointer past that)

procedure. Source: tween.odin:113

Tween_To :: proc{Tween_ToF32, Tween_ToVector, Tween_ToColor}

Adds a step that animates a value to to over duration seconds, from wherever it is when the step begins.

Inputs

  • tween: The tween
  • target: The value: an f32, a [2]f32 (positions, scales) or an sdl.Color (tints)
  • to: The value at the end
  • duration: Seconds
  • ease: The curve (easing.odin)

Returns

  • The tween, to chain more steps

Example

// Given: tween: ^e.Tween
e.Tween_To(tween, &node.position, node.position + {0, -40}, 0.5, .OutQuad)
e.Tween_To(tween, &node.tint, sdl.Color{255, 255, 255, 0}, 0.3)

procedure group. Source: tween.odin:147

Tween_ToF32 :: proc(tween: ^Tween, target: ^f32, to: f32, duration: f32, ease := EaseType.Linear) -> ^Tween

Tween_To for an f32

procedure. Source: tween.odin:150

Tween_ToVector :: proc(tween: ^Tween, target: ^[2]f32, to: [2]f32, duration: f32, ease := EaseType.Linear) -> ^Tween

Tween_To for a [2]f32 (positions, scales)

procedure. Source: tween.odin:155

Tween_ToColor :: proc(tween: ^Tween, target: ^sdl.Color, to: sdl.Color, duration: f32, ease := EaseType.Linear) -> ^Tween

Tween_To for an sdl.Color (each channel animated, rounded to the nearest value)

procedure. Source: tween.odin:160

Tween_FromTo :: proc{Tween_FromToF32, Tween_FromToVector, Tween_FromToColor}

Like Tween_To, from a given start (the value jumps to from when the step begins, and again on each loop).

Inputs

  • tween: The tween
  • target: The value: an f32, a [2]f32 (positions, scales) or an sdl.Color (tints)
  • from: The value at the start
  • to: The value at the end
  • duration: Seconds
  • ease: The curve (easing.odin)

Returns

  • The tween, to chain more steps

Example

// Given: tween: ^e.Tween
e.Tween_FromTo(tween, &node.tint, sdl.Color{255, 255, 255, 0}, sdl.Color{255, 255, 255, 255}, 0.5)

procedure group. Source: tween.odin:183

Tween_FromToF32 :: proc(tween: ^Tween, target: ^f32, from, to: f32, duration: f32, ease := EaseType.Linear) -> ^Tween

Tween_FromTo for an f32

procedure. Source: tween.odin:186

Tween_FromToVector :: proc(tween: ^Tween, target: ^[2]f32, from, to: [2]f32, duration: f32, ease := EaseType.Linear) -> ^Tween

Tween_FromTo for a [2]f32 (positions, scales)

procedure. Source: tween.odin:193

Tween_FromToColor :: proc(tween: ^Tween, target: ^sdl.Color, from, to: sdl.Color, duration: f32, ease := EaseType.Linear) -> ^Tween

Tween_FromTo for an sdl.Color

procedure. Source: tween.odin:200

Tween_Delay :: proc(tween: ^Tween, seconds: f32) -> ^Tween

Adds a pause before the next step.

Inputs

  • tween: The tween
  • seconds: Length of the pause (negative counts as 0)

Returns

  • The tween, to chain more steps

procedure. Source: tween.odin:216

Tween_Call :: proc(tween: ^Tween, callback: proc(user: rawptr), user: rawptr) -> ^Tween

Adds a step that calls callback(user) (once per play, including each loop), then goes on.

Inputs

  • tween: The tween
  • callback: Called when the step is reached
  • user: Passed to the callback (nil if unused); it must still be valid then

Returns

  • The tween, to chain more steps

Example

// Given: tween: ^e.Tween
e.Tween_Call(tween, proc(user: rawptr) { log.info("done") }, nil)

procedure. Source: tween.odin:236

Tween_Parallel :: proc(tween: ^Tween) -> ^Tween

Makes the next step added run together with the last one. Consecutive parallel steps form a group, which ends with its longest step. Does nothing when the tween has no steps yet.

Inputs

  • tween: The tween

Returns

  • The tween, to chain more steps

procedure. Source: tween.odin:251

Tween_SetLoops :: proc(tween: ^Tween, count: int) -> ^Tween

Sets how many more times the tween plays after the first. Each loop starts its steps again from the values as they are then (Tween_FromTo steps jump back to their start).

Inputs

  • tween: The tween
  • count: Extra plays after the first (0: once, -1: forever)

Returns

  • The tween, to chain more steps

procedure. Source: tween.odin:267

Tween_Pause :: proc(tween: ^Tween, paused: bool)

Pauses or resumes a tween where it is.

Inputs

  • tween: The tween
  • paused: true to pause, false to resume

procedure. Source: tween.odin:279

Tween_Kill :: proc(tween: ^Tween)

Stops a tween where it is; it’s freed at the scene’s next update. Its remaining callbacks don’t run.

Inputs

  • tween: The tween

procedure. Source: tween.odin:289

Tween_IsRunning :: proc(tween: ^Tween) -> bool

Tells whether a tween is still going. Only valid until the update that frees a finished tween: don’t call it on a pointer you kept longer.

Inputs

  • tween: The tween

Returns

  • true while it isn’t finished or killed (a paused tween is running)

procedure. Source: tween.odin:303

Ease :: proc(type: EaseType, t: f32) -> f32

Applies an easing curve to a progress value.

Inputs

  • type: The easing curve
  • t: Progress from 0 (start) to 1 (end). Values outside 0..1 are clamped

Returns

  • Eased progress. 0 at t = 0 and 1 at t = 1; Back and Elastic curves briefly go outside 0..1

Example

// Given: elapsed: f32, duration: f32, from: [2]f32, to: [2]f32
t := elapsed / duration
pos := from + (to - from) * e.Ease(.InOutCubic, t)

procedure. Source: easing.odin:49

CreateStateMachine :: proc(scene: ^Scene, settings: StateMachineSettings) -> ^StateMachine

Creates a StateMachine with these settings (copied) and adds it to the scene (at the start of the next update). It enters its start state on its first update.

Inputs

  • scene: The scene
  • settings: Its states, transitions and parameters

Returns

  • The state machine

procedure. Source: state_machine.odin:151

StateMachine_Current :: proc(sm: ^StateMachine) -> string

The current state’s name (“” before the first update, or without states).

procedure. Source: state_machine.odin:188

StateMachine_Is :: proc(sm: ^StateMachine, state: string) -> bool

Whether it’s in a state.

procedure. Source: state_machine.odin:195

StateMachine_TimeInState :: proc(sm: ^StateMachine) -> f32

Seconds since it entered its current state.

procedure. Source: state_machine.odin:202

StateMachine_StateIndex :: proc(sm: ^StateMachine, name: string) -> int

A state’s index by name (-1: none).

procedure. Source: state_machine.odin:209

StateMachine_Travel :: proc(sm: ^StateMachine, state: string) -> bool

Goes to a state now, whatever the transitions say (it’s entered again if it’s the current one).

Returns

  • false when there’s no such state

Example

// Given: brain: ^e.StateMachine
e.StateMachine_Travel(brain, "hurt")

procedure. Source: state_machine.odin:224

StateMachine_SetFloat :: proc(sm: ^StateMachine, name: string, value: f32)

Sets a Float parameter.

Example

// Given: brain: ^e.StateMachine, velocity: [2]f32
e.StateMachine_SetFloat(brain, "speed", abs(velocity.x))

procedure. Source: state_machine.odin:238

StateMachine_SetBool :: proc(sm: ^StateMachine, name: string, value: bool)

Sets a Bool parameter.

Example

// Given: brain: ^e.StateMachine, mover: ^e.CharacterMover
e.StateMachine_SetBool(brain, "grounded", mover.onFloor)

procedure. Source: state_machine.odin:249

StateMachine_SetTrigger :: proc(sm: ^StateMachine, name: string)

Sets a Trigger: the next transition its condition is part of is taken, and the trigger cleared.

Example

// Given: brain: ^e.StateMachine
e.StateMachine_SetTrigger(brain, "jump")

procedure. Source: state_machine.odin:260

StateMachine_GetParam :: proc(sm: ^StateMachine, name: string) -> f32

A parameter’s value (Bools and Triggers: 1 or 0).

procedure. Source: state_machine.odin:267

StateMachine_ConditionProblem :: proc(settings: ^StateMachineSettings, condition: string) -> string

Whether a condition is well formed for a set of parameters: “” when it is, else what’s wrong (editors show it).

Inputs

  • settings: The machine’s settings (its parameters)
  • condition: The condition

Returns

  • The problem, or “”

procedure. Source: state_machine.odin:283

StateMachineSettings_Clone :: proc(s: StateMachineSettings, allocator := context.allocator) -> StateMachineSettings

A deep copy of state machine settings.

procedure. Source: state_machine.odin:300

StateMachineSettings_Destroy :: proc(s: ^StateMachineSettings)

Frees state machine settings (the parser’s, StateMachineSettings_Clone’s) and empties them.

procedure. Source: state_machine.odin:314

StateMachineSettings_Equal :: proc(a, b: StateMachineSettings) -> bool

Whether two state machine settings are the same.

procedure. Source: state_machine.odin:339

CreateBone2D :: proc(scene: ^Scene, length: f32 = 32) -> ^Bone2D

Makes a bone (length pixels along its x axis)

procedure. Source: skeleton.odin:115

CreateSkeleton2D :: proc(scene: ^Scene) -> ^Skeleton2D

Makes a skeleton’s root

procedure. Source: skeleton.odin:122

CreateSkinnedMesh :: proc(scene: ^Scene, texture: ^sdl.Texture, mesh: MeshData, region := sdl.FRect{}) -> ^SkinnedMesh

Makes a skinned mesh: a picture bent over bones. Its mesh is copied into the node’s memory; it binds to its bones (their rest pose: where they are then) on its first update.

Inputs

  • scene: The scene
  • texture: The picture
  • mesh: Its triangles, the picture’s pixels under them, its bones and weights
  • region: The part of the texture (an atlas region; zero: all of it)

procedure. Source: skeleton.odin:136

CreateIKChain :: proc(scene: ^Scene, tip, target: string, settings := DEFAULT_IK_SETTINGS) -> ^IKChain

Makes an IK chain aiming the bones up from tip at target (paths from the IK node).

procedure. Source: skeleton.odin:161

SkinnedMesh_Bind :: proc(mesh: ^SkinnedMesh) -> int

Remembers where a skinned mesh’s bones are relative to it now: their rest pose. A vertex moves with its bones’ moves since. Done on its first update; call it again after changing the rest pose (editors do after moving bones) or the bones.

Returns

  • How many of its bones were found (missing ones leave their vertices in place)

procedure. Source: skeleton.odin:191

SkinnedMesh_Deform :: proc(mesh: ^SkinnedMesh) -> [][2]f32

Where a skinned mesh’s vertices are now (its space), its bones’ moves blended by their weights.

Returns

  • The vertices (temp allocated)

procedure. Source: skeleton.odin:219

Scene_SolveIK :: proc(scene: ^Scene)

Solves a scene’s IK chains (Scene_Update, after nodes and tweens): each turns its bones so the tip’s end reaches the target, or points at it when it’s out of reach.

procedure. Source: skeleton.odin:253

IKChain_Solve :: proc(c: ^IKChain) -> bool

Solves one IK chain now.

Returns

  • false when its tip or target isn’t found

procedure. Source: skeleton.odin:263

Bone2D_Tip :: proc(bone: ^Bone2D) -> [2]f32

A bone’s tip in the world

procedure. Source: skeleton.odin:292

MeshData_Clone :: proc(mesh: MeshData, allocator := context.allocator) -> MeshData

Copies mesh data into an allocator

procedure. Source: skeleton.odin:297

MeshData_Destroy :: proc(mesh: ^MeshData, allocator := context.allocator)

Frees what MeshData_Clone or ParseMesh made

procedure. Source: skeleton.odin:314

MeshData_Equal :: proc(a, b: MeshData) -> bool

Whether two meshes are the same

procedure. Source: skeleton.odin:325

FormatWeights :: proc(weights: []VertexWeights) -> string

A mesh’s weights as scene files write them: per vertex bone:weight pairs separated by spaces, vertices by ; (“0:1;0:0.5 1:0.5”).

Returns

  • The text (temp allocated)

procedure. Source: skeleton.odin:342

ParseWeights :: proc(text: string) -> ([]VertexWeights, bool)

Reads FormatWeights’s text (temp allocated); false when it doesn’t parse

procedure. Source: skeleton.odin:359

FormatTriangles :: proc(triangles: []i32) -> string

“0,1,2 2,3,0”: a mesh’s triangles (temp allocated)

procedure. Source: skeleton.odin:380

ParseTriangles :: proc(text: string) -> ([]i32, bool)

Reads FormatTriangles’s text (temp allocated); false when it doesn’t parse or isn’t whole triangles

procedure. Source: skeleton.odin:390

DragonBones_Convert :: proc(ske, tex: string, atlasPath, imagePath: string) -> (result: DragonBonesResult, ok: bool)

Converts a DragonBones armature and its texture atlas into a component and an atlas.

Inputs

  • ske: The _ske.json file’s text
  • tex: The _tex.json file’s text
  • atlasPath: Where the atlas will be (the sprites’ texture; “atlases/hero.atlas.xml”)
  • imagePath: Where the atlas’s picture will be (“atlases/hero.png”)

Returns

  • The component and atlas, what wasn’t converted, and false when the texts aren’t DragonBones files (the reason is the first warning)

procedure. Source: dragonbones.odin:57

ASEPRITE_SHEET_SUFFIX :: "#sheet"

The resource manager key suffix for an Aseprite file’s sheet of every frame (not its first frame)

constant. Source: animation_sets.odin:24

ASEPRITE_TILESET_SUFFIX :: "#tileset"

The resource manager key suffix for an Aseprite file’s tileset (#tileset0: its first; tilemap.odin)

constant. Source: animation_sets.odin:195

ANIMATOR_PROPERTIES := [?]AnimatorProperty{
{"position", .Vector, "Position", nil},
{"position.x", .Float, "Position X", nil},
{"position.y", .Float, "Position Y", nil},
{"rotation", .Float, "Rotation", nil},
{"scale", .Vector, "Scale", nil},
{"scale.x", .Float, "Scale X", nil},
{"scale.y", .Float, "Scale Y", nil},
{"tint", .Color, "Tint", nil},
{"opacity", .Float, "Opacity", nil},
{"visible", .Bool, "Visible", nil},
{"zIndex", .Float, "Z index", nil},
{"animation", .Text, "Animation", AnimatedSprite},
{"text", .Text, "Text", Label},
{"color", .Color, "Text color", Label},
{"energy", .Float, "Light energy", Light2D},
{"lightRadius", .Float, "Light radius", Light2D},
{"lightColor", .Color, "Light color", Light2D},
{"state", .Text, "State", StateMachine},
}

The built-in properties an Animator’s tracks animate, in the order tools list them. A script’s exported fields are animated as script.<field> besides these (Animator_ScriptFieldKind).

variable. Source: animator.odin:81

ANIMATOR_EVENT_PROPERTY :: "event"

The track property that fires the Animator’s onEvent instead of setting a value

constant. Source: animator.odin:103

ANIMATOR_SCRIPT_PREFIX :: "script."

The prefix of a script field’s property (“script.speed”)

constant. Source: animator.odin:106

ANIMATOR_SHADER_PREFIX :: "shader."

The prefix of a shader parameter’s property (“shader.amount”: shaders.odin; floats, vec2s and colors animate)

constant. Source: animator.odin:109

DEFAULT_ANIMATOR_SETTINGS :: AnimatorSettings{speed = 1}

An Animator’s settings when the scene file sets none

constant. Source: animator.odin:159

DEFAULT_IK_SETTINGS :: IKSettings{length = 2, bendPositive = true, enabled = true}

An IK chain’s settings when none are given (CreateIKChain, scene files): two bones, bending clockwise, on

constant. Source: skeleton.odin:103