Skip to content

Animation graphs

Animation::Graph describes named states and transitions for one character. Install it with game_object.animation.use(graph). A graph can be reused: each character owns its clocks and parameter values. Installation validates the whole definition before changing the character.

graph = Animation::Graph.new(initial: :grounded, parameters: {speed: 0.0, grounded: true})
graph.blend_1d(:grounded, parameter: :speed,
               clips: [[0.0, :Idle], [1.5, :Walk], [5.0, :Run]])
graph.clip(:airborne, :Jump, loop: false)
graph.transition(:grounded, :airborne, parameter: :grounded, equals: false, fade: 0.15)
graph.transition(:airborne, :grounded, parameter: :grounded, equals: true, fade: 0.2)
animation.use(graph)

# In the character's update:
animation.set(:speed, velocity.length)
animation.set(:grounded, grounded?)

Declare every parameter and its default in parameters:. Values are booleans or finite numbers; a parameter keeps its declared type. Blend parameters must be numeric. Clips must exist in the loaded scene, state names must be unique, and every state must be reachable from initial. For numeric conditions use exactly one of equals:, above: or below:; comparisons are strict for above and below. For booleans use equals:. The first matching transition in declaration order wins, and at most one transition is taken per frame.

One-dimensional blend points must have strictly increasing finite thresholds. Outside their range, the nearest endpoint plays alone. Inside, the two neighboring clips mix linearly. Clips share normalized phase; their weighted duration determines the cycle length. A missing channel contributes the character's rest pose. Rotations mix normalized quaternions on the same hemisphere. Installation and parameter updates reject synchronized clip speeds outside the representable float range, preserving the active controller and its previous parameter values.

For movement on two axes, use a complete rectangular grid. Points can be in any order; each combination of the distinct x and y coordinates must appear exactly once, with at least two coordinates per axis. Both parameters must be distinct, declared numbers. Missing corners, duplicate points, unknown clips and nonfinite coordinates are rejected at installation.

graph = Animation::Graph.new(initial: :move, parameters: {strafe: 0.0, forward: 0.0})
graph.blend_2d(:move, parameters: [:strafe, :forward], clips: [
  [[-1.0, -1.0], :BackLeft], [[1.0, -1.0], :BackRight],
  [[-1.0, 1.0], :ForwardLeft], [[1.0, 1.0], :ForwardRight]
])
animation.use(graph)
animation.set(:strafe, sideways_speed)
animation.set(:forward, forward_speed)

The four corners of the enclosing cell mix bilinearly. Outside the grid, each axis clamps to its nearest endpoint. Zero-weight corners are omitted; shared phase and weighted cycle duration work as for one-dimensional blends. The evaluated tracks are replicated by NetworkSync. The larger snapshots use protocol version 2; machines with different protocols cannot join.

clip, blend_1d and blend_2d accept loop: and speed:. Negative speed plays backwards; a nonlooping state holds its endpoint. State fades keep the source and destination playing and use at most eight clips (four per side for two-dimensional states). A fade completes before another transition is evaluated at the next frame boundary; changing parameters during a fade does not interrupt it.

animation.state returns the current destination state's name as a String, including during a fade, or nil under manual control. clip and time refer to the dominant destination clip; equal weights choose the earlier blend point (grid points are ordered by y, then x). pause freezes the graph's clocks and fade. Calling use again installs a fresh controller with its defaults.

mix and unmix retain the graph and affect the separate overlay as usual. A valid manual play or follow_scene detaches the graph immediately. Fades across this control boundary are not supported; fades between graph states are. An invalid manual clip request preserves the active graph.

NetworkSync sends the evaluated clips, times, weights and speeds, including remaining fade time. Receivers advance those tracks without installing a graph or evaluating its conditions. Only the owner changes parameters. Every machine needs the same clips; malformed or unknown graph tracks are ignored without replacing the active controller. Graphs currently support flat clip states, one-dimensional blends and rectangular two-dimensional grids; there are no hierarchical trees or irregular point-cloud blends.