- C# 76.1%
- Rust 17.6%
- Lua 4%
- Shell 1.2%
- Nix 0.8%
- Other 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .gitea/workflows | ||
| .github | ||
| csharp | ||
| rust | ||
| types | ||
| vpm | ||
| .envrc | ||
| .gitignore | ||
| .luarc.json | ||
| AGENTS.md | ||
| DOCUMENTATION.md | ||
| flake.lock | ||
| flake.nix | ||
| justfile | ||
| LICENSE | ||
| MigratingFromV0ToV1.md | ||
| old_README.md | ||
| README.md | ||
| WRITING_SCRIPTS.md | ||
Animator As Crab
A hard fork of Animator As Code where animator controllers are written in Lua instead of C#.
A Lua script is evaluated by a Rust library (libaac), which validates it and emits the whole
controller as JSON. A Unity Editor window deserializes that JSON and builds a real
AnimatorController through the original Animator As Code V1 library, which is left untouched.
script.lua --> libaac (Rust) --JSON--> AacCrabWindow (Unity Editor) --> AnimatorController
Layout
| Path | Contents |
|---|---|
rust/ |
The Lua DSL, graph model, validator, and the libaac cdylib. |
rust/examples/avatar.lua |
The reference script, exercising every feature of the DSL. |
csharp/dev.doloro.animator-as-crab/ |
The Unity package: the untouched Animator As Code V1 library and the bridge in V1/Editor/Crab/. |
flake.nix |
Dev shell with the Rust toolchain, dotnet, mono, and jq. |
Building
nix develop
cargo test --manifest-path rust/Cargo.toml
cargo run --bin aac-dump -- rust/examples/avatar.lua # print the generated JSON
nix build .#aac # libaac.so + aac-dump
Copy libaac.so (or libaac.dll, libaac.dylib) into the Unity project's Assets/Plugins folder,
and add csharp/dev.doloro.animator-as-crab to the project's packages.
Writing a script
local aac = AnimatorAsCode()
aac:system_name("MyAvatar")
aac:asset_key("AAC_")
local speed = aac:float_param("Speed", 0.0)
local ctrl = aac:new_controller()
local base = ctrl:layer("Base")
local idle = base:state("Idle", 0, 0)
local walk = base:state("Walk", 1, 0)
local walk_clip = aac:clip("walk_anim")
walk_clip:toggle("Body/Props", true)
walk:set_clip(walk_clip)
idle:transition_to(walk):when(speed:gt(0.1)):no_exit_time():duration(0.25)
Conditions are written as method calls on the parameters themselves (speed:gt(0.1),
is_sitting:eq(true)). To combine multiple conditions use
when_all({speed:lt(0.05), is_sitting:eq(false)}).
See rust/examples/avatar.lua for the full surface: parameters, clips, curves, blend trees,
sub-state machines, any-state transitions, and transition settings.
Pre-made clips (animation store)
An animation store seeds clips from AnimationClip assets that already exist in the project.
store:clip(name) resolves to <folder>/<name>.anim and returns a motion usable anywhere a
generated clip is: as a state motion, as a blend tree child, or through add_motion_direct. A store
clip can be edited like any other clip.
local store = aac:AnimationStore("Assets/Doloro/Clips")
local walk = store:clip("Walk") -- Assets/Doloro/Clips/Walk.anim
walk:looping(true)
walk:keyframe("Body/Props", "m_IsActive", 0.0, 0.0)
walk_state:set_clip(walk)
locomotion:add_motion(walk, 0.0)
The generator loads the asset with AssetDatabase.LoadAssetAtPath, clones it into the asset
container, and applies the script's keyframes and looping on the clone: the pre-made asset is never
modified. A store clip with no looping(...) call keeps the source asset's own looping setting.
Requesting the same asset twice reuses the one reference, and a missing asset is a clear error.
Generating
Open Tools > Animator As Crab and fill in the window:
- the path to the
.luascript (it is watched, so saving the script regenerates the controller), - the
AnimatorControllerto generate into, - the animator root, the asset container, and the container mode,
- whether states should write defaults.
Nothing is defaulted silently, and the controller is rebuilt with Animator As Code's modification API: the controller is cleared, and the clips and blend trees of the same asset key are removed from the container before being recreated. Animator As Code's own C# API is untouched and still usable on the same controller.
Limitations
- The Unity side has not been run: there is no Unity project in this repository, so the bridge and the generator are verified by reading, not by executing.
- Blend trees can only nest a blend tree that was declared earlier in the script.
- The graph carries no avatar masks, layer weights, state behaviours, or parameter drivers yet.