ludic.anim: animset_choice names the playing choice's clip as the set does, animset_can says whether the rig carries any of a state's clips (a one-shot with none is not asked for)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 20:19:05 +03:00
parent a22b8a572b
commit a87f20c66e
6 changed files with 35 additions and 3 deletions

View file

@ -68,7 +68,9 @@ and transitions (from a state or `"*"`, with a fade; with `@frame when: fn() ->
own the frame it answers true). The code binds an `AnimPlayer` to a set and a rig
(`animset_bind`), sets parameters (`animset_param`), asks for a state (`animset_request`, taken once
however often it is asked) and ticks it into a skin (`animset_tick`). A one-shot (`loop: false`)
holds its last pose and hands on to `next`. Clip names are made once per rig; a tick makes nothing.
holds its last pose and hands on to `next`. `animset_choice` names the playing choice's clip as the set
does ("sit_seat", no rig), and `animset_can(key)` says whether the rig carries any of a state's clips, so a
one-shot with none yet is not asked for. Clip names are made once per rig; a tick makes nothing.
## Tests

View file

@ -15,6 +15,17 @@ export function animset_request(pl: AnimPlayer, key: string) -> void {
pl.want = i
}
# the state is in the set and the rig carries one of its clips: a one-shot with none is not asked for
export function animset_can(pl: AnimPlayer, key: string) -> bool {
if pl.set < 0 or pl.clips == null { return false }
let i = as_state(AnimSets[pl.set], key)
if i < 0 { return false }
let d = AnimSets[pl.set].states[i]
if d.choices == null { return false }
for c in 0 .. len(d.choices) { if pl.clips[pl.base[i] + c] != null { return true } }
return false
}
function as_param_at(pl: AnimPlayer, name: string) -> int {
if pl.set < 0 { return -1 }
let s = AnimSets[pl.set]

View file

@ -20,10 +20,16 @@ export property AnimPlayer {
mix: float = 1.0
fade: float = 0.2
rate: float = 1.0
pick: int = -1 # which of the state's choices plays
}
export function animset_clip(pl: AnimPlayer) -> Clip { return pl.clip }
export function animset_time(pl: AnimPlayer) -> float { return pl.t }
export function animset_rate(pl: AnimPlayer) -> float { return pl.rate }
# the playing choice's clip as the set names it, without the rig ("sit_seat"); "" before a tick
export function animset_choice(pl: AnimPlayer) -> string {
if pl.set < 0 or pl.pick < 0 { return "" }
return AnimSets[pl.set].states[pl.state].choices[pl.pick].clip
}
export function animset_ready(pl: AnimPlayer) -> bool { return pl.ready }
export function animset_bound(pl: AnimPlayer, set: int, rig: string) -> bool { return pl.set == set and pl.rig == rig and pl.names != null }

View file

@ -6,6 +6,7 @@ export function animset_tick(anim_st: mut AnimState, pl: AnimPlayer, sk: Skin, d
let d = AnimSets[pl.set].states[pl.state]
let k = as_pick(pl, d)
if k < 0 { return false }
pl.pick = k
pl.rate = as_rate(pl, d, d.choices[k])
as_switch(pl, d, pl.clips[pl.base[pl.state] + k])
let adv = dt * pl.rate

View file

@ -113,4 +113,15 @@ program AnimSetTest {
}
expect(playing(pl) == "rig_idle")
}
test "the playing choice is named as the set names it; a state the rig has no clip for cannot be asked" (anim_st: mut AnimState, render3d_st: mut Render3dState) {
let sk = skin(render3d_st)
let pl = rig(anim_st)
expect(animset_choice(pl) == "")
animset_param(pl, "speed", 1.0)
animset_tick(anim_st, pl, sk, 0.016)
expect(animset_choice(pl) == "walk")
expect(animset_can(pl, "wave"))
expect(not animset_can(pl, "bow"))
expect(not animset_can(pl, "nope"))
}
}

View file

@ -1,5 +1,5 @@
# sets.lres - the test's one set: a ground state picking by speed bands, a one-shot that hands back,
# and a hop the code never asks for, taken when toy_hops says so
# a hop the code never asks for, taken when toy_hops says so, and a bow no rig carries
toy {
entry: "ground"
params: ["speed"]
@ -14,7 +14,8 @@ toy {
]
},
{ key: "wave", loop: false, next: "ground", choices: [{ clip: "wave" }] },
{ key: "hop", choices: [{ clip: "hop" }] }
{ key: "hop", choices: [{ clip: "hop" }] },
{ key: "bow", loop: false, next: "ground", choices: [{ clip: "bow" }] }
]
transitions: [{ from: "ground", to: "hop", blend: 0.1, when: fn toy_hops }]
}