Runtimes
Rigby2D animations play in games and apps through the included runtimes. All of them load skeleton
.json (3.8, 4.2, 4.3) or binary .skel (4.3) with an .atlas, and evaluate meshes, weights, skins,
image sequences, clipping, IK, transform, path, physics and slider constraints, draw order, events and
layered tracks with crossfades. They also play scenes: several
characters on a stage with a moving camera and parallax.
| Runtime | Language | Status |
|---|---|---|
JavaScript (src/core) + WebGL renderer (src/webgl) | JavaScript, no dependencies | Complete |
Rust (runtimes/rust/rigby-runtime) | Rust, no dependencies; 3 to 4× faster | Complete |
Unity (RigbySkeleton, RigbyScene) | C#, every platform Unity builds for; multi-core, no garbage | Complete |
Godot (RigbySkeleton2D, RigbyScene2D) | GDExtension on the Rust core | Preview |
The landing page of the website plays NILA with the JavaScript runtime.
#JavaScript
Copy src/core/ into your project (and src/webgl/ for a ready-made renderer):
import { AnimationState, Pose, parseAtlas, readSkeletonJson } from './core/index.js';
import { WebGLRenderer } from './webgl/renderer.js';
const { skeleton } = readSkeletonJson(JSON.parse(jsonText), 'hero'); // hero.json
const pose = new Pose(skeleton);
const state = new AnimationState();
state.play(skeleton, 'walk', true, 0.2); // track 0, 0.2 s crossfade
state.setAnimation(skeleton, 1, 'shoot', false, 0.1); // track 1 layers on top
state.addAnimation(skeleton, 0, 'idle', true, 0.3); // queued after walk
const renderer = new WebGLRenderer(gl);
const textures = renderer.atlasTextures(parseAtlas(atlasText), { 'hero.png': pageImage });
function frame(dt) {
state.update(skeleton, dt);
state.apply(skeleton, pose);
renderer.begin(canvas.width, canvas.height, { x: 0, y: 200, zoom: 1 });
renderer.drawSkeleton(skeleton, pose, textures);
renderer.end();
}For .skel files use readSkeletonJson(readSkeletonBinary(bytes), 'hero'). With another renderer,
draw pose.quads(skeleton): world-space positions (Y up), UVs, tint and blend mode per region and
mesh. examples/webgl.html is a small player (npm run web:dev, then open /examples/webgl.html).
#Events
state.addListener({
start: (entry) => {},
complete: (entry) => {},
event: (entry, e) => playSound(e.audio ?? e.name, e.volume), // { name, time, int, float, string, audio, volume, balance }
});Physics simulates while AnimationState updates; pose.resetPhysics() starts it over.
#Rust
use rigby_runtime::{load_skeleton, Atlas, AnimationState, Pose, RegionLookup, RenderOutput};
let data = load_skeleton(&std::fs::read("hero.skel")?)?;
let atlas = Atlas::parse(&std::fs::read_to_string("hero.atlas")?);
let lookup = RegionLookup::new(&data, &atlas);
let (mut pose, mut state, mut out) = (Pose::new(&data), AnimationState::new(), RenderOutput::default());
state.play(&data, "walk", true, 0.2);
// Every frame:
state.update(&data, dt);
state.apply(&data, &mut pose);
out.build(&data, &pose, &lookup, [1.0; 4]);
for batch in out.batches() { /* positions, UVs, colors, indices for atlas page `batch.page` */ }Scenes: rigby_runtime::scene::load_scene(&manifest, |file| std::fs::read(dir.join(file))) returns a
ScenePlayer (play, update, draw_list).
It matches the JavaScript runtime frame by frame (golden tests, including a scene). Build and test with
cargo test --release --workspace in runtimes/rust. See the
runtimes README.
#Unity
The com.rigby2d.unity package for Unity 2021.3 or newer (Unity 6 included), Built-in or
Universal render pipeline. In Unity: Window ▸ Package Manager ▸ + ▸ Install package from git URL…
https://github.com/jaisurya-dev-art/rigby2d.git?path=/runtimes/unity/com.rigby2d.unityDrop an export (.skel or .json, .atlas and its .png) into Assets: the .atlas becomes a
skeleton asset. Drag it into the scene, pick an animation, and it plays (in the editor too). Scenes
exported with File ▸ Export ▸ Scene for runtimes import the same way and play with the RigbyScene
component, which can drive a camera.
var hero = GetComponent<RigbySkeleton>();
hero.Play("walk", loop: true, mix: 0.2f);
hero.AddAnimation(0, "idle", loop: true, mix: 0.3f);
hero.AnimationEvent += (s, e) => { if (e.Name == "footstep") step.Play(); };
gun.position = hero.GetBoneWorldPosition("gun-tip");- Every core, no garbage: playing skeletons pose and build their meshes in parallel, and steady playback allocates nothing. 200 NILAs (39 bones, 1,002 skinned vertices each) take 5.0 ms a frame in an IL2CPP player on a 4-core laptop, upload included: 25 µs a character.
- Tested in Unity 6.3 (Built-in and Universal render pipelines) and 2022.3, in IL2CPP and Mono players.
- Clean edges in Gamma and Linear color space: the shader handles straight or premultiplied atlases itself, with two-color tint and every blend mode.
- The same animation as the editor: the C# runtime is tested frame by frame against the JavaScript runtime, like the Rust one.
See the Unity guide for every component, property and event.
#Godot
The RigbySkeleton2D node for Godot 4.3+ (preview; prebuilt for Windows x64, build from source
elsewhere). Install the rigby2d-godot-addon zip from the
Releases into your project, add the node, and
set Skeleton File, Atlas File and Animation.
@onready var hero: RigbySkeleton2D = $Hero
func _ready() -> void:
hero.animation_event.connect(_on_event)
hero.play("walk", true, 0.2)
hero.queue(0, "idle", true, 0.3, -1.0)For scenes, the RigbyScene2D node plays a .scene.json export: the stage, every character
and their clips, with the stage's camera available for a Camera2D. See the
Godot guide for every property and method.
#Performance
One character, one frame (apply, constraints, world transforms and vertices), median of 2,000 frames:
| Rig | JavaScript | Rust |
|---|---|---|
| 67 bones, 276 mesh vertices | 50 µs | 14.5 µs |
| 76 bones, 667 weighted vertices, 9 IK | 71 µs | 22 µs |
| 143 bones, 879 vertices, 21 transform/path constraints | 90 µs | 22 µs |