Animations blend out on Stop and at the end of Once (reset), and Play can restart a running animation (#106)
Stop animation gets a blend (s) port that fades the animation back to what lies underneath. Play animation gets blend out (s) for Once (reset) and a restart port that starts a running, paused or fading animation from frame 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8a377d9239
commit
195fc94e08
4 files changed
+369
-29
No files matched your search
@@ -37,7 +37,7 @@ interface Binding {
|
||||
|
||||
/**
|
||||
* Plays what `Play animation` started. Players run in start order, so of two setting one property
|
||||
* the later start wins – or, while it blends in, mixes over what was there. Runs before the
|
||||
* the later start wins – or, while it blends in or fades out, mixes over what was there. Runs before the
|
||||
* BehaviorSystem: a behavior writing the same property writes last, and hears this frame's `finished`.
|
||||
*/
|
||||
export class AnimationSystem implements System {
|
||||
@@ -67,31 +67,53 @@ export class AnimationSystem implements System {
|
||||
// The first tick only takes the starting values and shows frame 0; the clock runs from the next.
|
||||
player.bases = {};
|
||||
for (const b of bindings) player.bases[b.base] = b.read();
|
||||
} else if (player.playing && isHandleEnabled(entity)) {
|
||||
player.time += dt;
|
||||
if (blend) blend.elapsed += dt;
|
||||
} else if (isHandleEnabled(entity)) {
|
||||
if (player.playing) {
|
||||
player.time += dt;
|
||||
if (blend) blend.elapsed += dt;
|
||||
}
|
||||
// A paused player that was stopped with a blend still fades out.
|
||||
if (player.fade) player.fade.elapsed += dt;
|
||||
}
|
||||
const head = animationPlayheadAt(asset, player.time);
|
||||
const share = blend ? blendShare(blend) : 1;
|
||||
const fade = player.fade;
|
||||
const fadeShare = fade ? blendShare(fade) : 0;
|
||||
for (const b of bindings) {
|
||||
let value = trackWriteAt(b.track, b.kind, head.frame, player.bases[b.base], head.reverse);
|
||||
if (value === undefined) continue;
|
||||
const below = written.has(b.base) ? b.read() : undefined;
|
||||
if (share < 1 && blends(b.kind)) {
|
||||
const from = written.has(b.base) ? b.read() : blend?.from?.[b.base];
|
||||
const from = below ?? blend?.from?.[b.base];
|
||||
if (from !== undefined) value = blendValue(b.kind, from, value, share);
|
||||
}
|
||||
if (fade) {
|
||||
const back = below ?? player.bases[b.base];
|
||||
if (back !== undefined) value = blends(b.kind) ? blendValue(b.kind, value, back, fadeShare) : back;
|
||||
}
|
||||
b.write(value);
|
||||
written.add(b.base);
|
||||
}
|
||||
if (blend && share >= 1) delete player.blend;
|
||||
if (fade) {
|
||||
if (fadeShare >= 1) drop(entity, player);
|
||||
continue;
|
||||
}
|
||||
if (!head.finished) continue;
|
||||
if (playbackOf(asset) === 'once-reset')
|
||||
for (const b of bindings) {
|
||||
const base = player.bases[b.base];
|
||||
if (base !== undefined) b.write(base);
|
||||
}
|
||||
drop(entity, player);
|
||||
this.finished.push({ entity, animationId: asset.id });
|
||||
if (playbackOf(asset) !== 'once-reset') {
|
||||
drop(entity, player);
|
||||
continue;
|
||||
}
|
||||
if (player.blendOut) {
|
||||
player.fade = { seconds: player.blendOut, elapsed: 0, ended: true };
|
||||
continue;
|
||||
}
|
||||
for (const b of bindings) {
|
||||
const base = player.bases[b.base];
|
||||
if (base !== undefined) b.write(base);
|
||||
}
|
||||
drop(entity, player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user