Animations blend out on Stop and at the end of Once (reset), and Play can restart a running animation (#106)
game-maker / gate (push) Successful in 1m33s
game-maker / suite (push) Successful in 5m37s

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:
adminandClaude Opus 5 committed 2026-09-17 11:50:10 +02:00
1 parent 8a377d9239
commit 195fc94e08
4 files changed
+369 -29

No files matched your search

+33 -11
View File
@@ -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);
}
}