ludic.anim: animation sets - the game's src/animset moved in as it stood (records, AnimPlayer, animset_bind / request / param / tick and the accessors); AnimSets is an open @ByKey registry a game fills with def AnimSets from; a test on a rig of hand-made clips
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d9a73d63bd
commit
5517873275
10 changed files with 442 additions and 1 deletions
52
packages/ludic.anim/set_tick.ludic
Normal file
52
packages/ludic.anim/set_tick.ludic
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# set_tick.ludic - a frame of a player: follow the graph, pick the clip, cross-fade on a change,
|
||||
# advance both clips by the same step and play them into the skin (ludic.anim)
|
||||
export function animset_tick(anim_st: mut AnimState, pl: AnimPlayer, sk: Skin, dt: float) -> bool {
|
||||
if not pl.ready or sk == null { return false }
|
||||
as_follow(pl)
|
||||
let d = AnimSets[pl.set].states[pl.state]
|
||||
let k = as_pick(pl, d)
|
||||
if k < 0 { return false }
|
||||
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
|
||||
pl.t = pl.t + adv
|
||||
pl.pt = pl.pt + adv
|
||||
as_one_shot(pl, d)
|
||||
if pl.mix < 1.0 { pl.mix = Math.min(1.0, pl.mix + dt / pl.fade) }
|
||||
# anim_play blends TOWARD its second clip, so the weight is how much of the old one is left
|
||||
return anim_play(anim_st, sk, pl.clip, pl.t, pl.prev, pl.pt, 1.0 - pl.mix)
|
||||
}
|
||||
|
||||
# a new clip fades in from the old one. Within a state it carries the PHASE across: two leg cycles
|
||||
# restarted at zero put the far leg forward for the length of the fade, which reads as a stumble.
|
||||
function as_switch(pl: AnimPlayer, d: AnimStateDef, want: Clip) -> void {
|
||||
var fade = d.blend
|
||||
if pl.fade_next > 0.0 { fade = pl.fade_next }
|
||||
pl.fade_next = -1.0
|
||||
if pl.clip == null {
|
||||
pl.clip = want
|
||||
pl.t = 0.0
|
||||
pl.prev = null
|
||||
pl.mix = 1.0
|
||||
return
|
||||
}
|
||||
if want == pl.clip { return }
|
||||
var ph = 0.0
|
||||
if d.keep_phase and not pl.fresh and pl.clip.dur > 0.0 { ph = pl.t % pl.clip.dur / pl.clip.dur }
|
||||
pl.prev = pl.clip
|
||||
pl.pt = pl.t
|
||||
pl.clip = want
|
||||
pl.t = ph * want.dur
|
||||
pl.fade = fade
|
||||
pl.mix = 0.0
|
||||
if not (fade > 0.0) { pl.mix = 1.0 }
|
||||
}
|
||||
|
||||
# a clip that does not loop holds its last pose, and hands over to its `next` state when played
|
||||
function as_one_shot(pl: AnimPlayer, d: AnimStateDef) -> void {
|
||||
if d.loop or pl.clip.dur <= 0.0 or pl.t < pl.clip.dur { return }
|
||||
pl.t = pl.clip.dur * 0.999
|
||||
if d.next == "" { return }
|
||||
let i = as_state(AnimSets[pl.set], d.next)
|
||||
if i >= 0 { pl.want = i }
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue