Axel Meyer 8013516ce8 feat: extend rig-format with texture/z_order/scale/anchor fields
Additive extension to build_rig output, preserving backward-compat
with existing rigs that lack the new fields:

- bone.texture: optional atlas-id (string) for sprite-mode rendering;
  resolved to a texture-handle by puppet.load_textures(). When absent
  the bone falls back to colored-rect render via draw_rect_rotated.
- bone.z_order: render-sort key (lower = back, higher = front);
  default 0.
- bone.scale: [sx, sy] pair; default [1, 1]; negative components
  mirror the sprite.
- bone.anchor: optional [x, y] override of the atlas-supplied anchor.
- rig.asset_pack: alias-key into the consumer module's asset_aliases
  manifest field; lets puppet.load_textures resolve atlas-ids to
  texture-handles via the asset-lib.
- rig.bones_z_sorted: array of bones pre-sorted by z_order; render
  walks this once per frame.

Version bumped to 0.2.0. Subsequent commits add multi-level parent-
chain transform, sprite-mode render, and load_textures.
2026-05-18 02:53:47 +02:00

lib-core.puppet

Skeletal animation primitive for top-down 2D characters: bones, tracks, rest pose, keyframe animations, procedural animations (Lua-side), and look-at constraint. No footplant IK in v0.1 (deferred).

Version: 0.1.0 Lib-ID: lib-core.puppet Requires: (none — pure Lua + engine.render.*) Tags: animation, skeleton, puppet, character

Topology

API

puppet.build_rig(rig_table)

Validates a rig table (parsed from *.rig.json) and returns an internal representation with bones_by_id, parent-resolved object references, and track-membership maps. Raises on invalid data (missing parents, bones in 2 tracks, etc.).

puppet.build_animation(anim_table, rig)

Validates an animation table against a built rig (bones referenced in keyframes must exist and be in the animation's declared track).

puppet.sample_animation(animation, t)

Returns a frame { <bone_id> = { angle = <number>, ... }, ... } sampling the animation at time t with linear interpolation. Wraps on t > duration if animation.loop is true.

puppet.spawn(rig, { x, y }), puppet.despawn(handle)

Spawn a new puppet instance at world position (x, y). Returns an opaque handle. despawn removes the instance.

puppet.update(dt), puppet.render(handle)

update(dt) runs the pipeline for ALL spawned puppets (rest → look-at → keyframes → procedural). render(handle) draws ONE puppet via engine.render.draw_rect_rotated per bone.

puppet.set_look_target(p, world_x, world_y), puppet.clear_look_target(p)

Set or clear the look-at target. Bones marked look_at: true in the rig rotate to face the target.

puppet.bone_angle(p, bone_id)

Returns the current world-space angle of bone_id (after the full pipeline tick). Used by tests + by callers needing bone-state.

puppet.set_procedural(p, name, callback), puppet.clear_procedural(p, name)

Register or unregister a per-frame procedural callback. Callback signature: function(handle, dt). Inside the callback, mutate bones via puppet.write_bone(handle, bone_id, { angle = <number> }).

puppet.play(p, animation_name, { loop, speed }), puppet.stop(p, animation_name), puppet.stop_all(p), puppet.is_playing(p, animation_name)

Keyframe-layer controls. Animation must be registered via puppet.register_animation(p, anim) first.

puppet.register_animation(p, animation)

Binds a built animation (from build_animation) to a puppet instance, making it available to play.

puppet.load_rig(path), puppet.load_animation(path)

Convenience wrappers: read JSON file via engine.asset.read_file + parse + call build_rig / build_animation.

puppet.position(p), puppet.facing(p), puppet.move_to(p, x, y)

Puppet owns its world position. move_to sets it instantly (caller applies speed). facing returns the last-movement-direction angle.

puppet.write_bone(handle, bone_id, { angle })

Procedural-callback-only API to mutate a bone. Validates track ownership at write time.

Conventions

  • World-coords + pixel-units throughout (per ADR-0031).
  • Bone angles in radians (internal). JSON rest-angles and animation keyframe angles are in degrees, converted on load.
  • Update pipeline runs once per frame (puppet.update(dt)) for ALL spawned puppets. render is per-puppet so callers can interleave with other rendering.

CHANGELOG

v0.1.0

  • Initial release: skeleton + bones + tracks + rest + keyframe + procedural + look-at constraint. Footplant IK deferred.

References

  • architecture/puppet.md (Reference)
  • ADR-0037 (Tests-as-Libs)
  • ADR-0038 (API-Doc-Convention)
Description
Skeletal animation primitive: bones + tracks + keyframes + procedural + look-at
Readme 319 KiB
Languages
Lua 100%