Skip to content

fix: emit animation_finished only when all loops complete - #222

Merged
Naruto merged 3 commits into
developfrom
fix/loop-finish-events
Jul 15, 2026
Merged

Naruto merged 3 commits into
developfrom
fix/loop-finish-events

Conversation

@Naruto

@Naruto Naruto commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

animation_finished was wired straight to ss_runtime_is_end_frame_reached, which does not mean "playback finished" — it means "the last frame of a cycle is showing". The runtime raises it once per cycle with a finite loop count, and never under the default infinite count.

So set_loop_count(3) emitted finished, finished, finished instead of looped, looped, finished.

Switches to ss_runtime_is_finished, the terminal signal added in cri-middleware/SpriteStudio-SDK#307.

Notes for review

  • is_finished is a sticky state, not a pulse. The existing if (!ss_runtime_is_playing(runtime_ctx)) return; at the top of update() is what keeps it from re-emitting, since only play() clears it. That dependency is now called out in a comment rather than left implicit.
  • animation_looped is deliberately unchanged. It still fires on every wrap, including under an infinite loop count — that is where cycle hooks (idle/walk beats, lap counting) are most useful, and it costs one signal per cycle, not per frame.

Doc corrections

Both were wrong before this PR, independently of the code:

  • set_loop_count(0) was documented as "plays once with no repeat" / 「0 で1回だけ再生」 and 0 for no playback. 0 is a synonym for infinite — the runtime normalizes every value <= 0 to -1.
  • animation_finished was documented as "non-looping only", which is not what it did (it fired per cycle) nor what it does now (it fires once all loops complete).

Verification

Built against the SDK branch and run headless (examples/overall, module build):

set_loop_count emitted signals
3 looped, looped, finished
1 finished
-1 looped, looped, … (never finishes)
0 identical to -1

Depends on cri-middleware/SpriteStudio-SDK#307

Merge #307 first. This PR does not build without it: ss_runtime_is_end_frame_reached is removed there, and ss_runtime_is_finished only exists there.

The SDK submodule pins dc9b987, the head of #307's branch — deliberately, and it stays that way. CI builds the runtime from the submodule (scripts/build-runtime.sh), so this PR is green as-is, and it is verified against exactly the SDK revision it is written for. #307 merges as a merge commit (the SDK repo keeps deleteBranchOnMerge=false and every recent PR landed that way), so dc9b987 remains reachable from the default branch afterwards and the pin does not rot.

scripts/SDK_VERSION.txt stays at v7.0.0-alpha.1: it can only name a release that exists, and none ships ss_runtime_is_finished yet. It is read by the download path (scripts/download-sdk.sh, used by weekly.yml), not by PR CI. Bump it when that release is cut — until then that path is on the pre-fix SDK.

Naruto added 3 commits July 15, 2026 19:11
`ss_runtime_is_end_frame_reached` means "the last frame of a cycle is
showing", not "playback finished". With a finite loop count the runtime
raises it once per cycle, and under the default infinite count it is never
raised at all -- so wiring `animation_finished` straight to it fired the
signal once per loop iteration. `set_loop_count(3)` emitted three
`animation_finished` instead of `looped, looped, finished`.

Switch to `ss_runtime_is_finished`, the terminal signal added upstream. It is
a sticky state rather than a pulse; the existing `is_playing` early-return is
what keeps it from re-emitting, so note that dependency where it matters.

`animation_looped` is unchanged -- it still fires on every wrap, including
under an infinite loop count, which is where cycle hooks are most useful.

Also corrects the docs, which claimed `0` plays once (it is a synonym for
infinite) and that `animation_finished` is emitted for non-looping animations
only.

Verified headless against this SDK branch:

  set_loop_count(3)  -> looped, looped, finished
  set_loop_count(1)  -> finished
  set_loop_count(-1) -> looped, looped, ... (never finishes)
  set_loop_count(0)  -> identical to -1

The SDK submodule is bumped to the branch of cri-middleware/SpriteStudio-SDK#307
so CI can build the runtime; it needs re-pointing at develop once that merges.
Picks up the end-frame event-duplication fix, so CI builds the runtime this PR
is actually written against. Still re-point at the default branch once #307 lands.
@Naruto
Naruto marked this pull request as ready for review July 15, 2026 11:05
@Naruto
Naruto merged commit 9bdd070 into develop Jul 15, 2026
2 of 4 checks passed
@Naruto
Naruto deleted the fix/loop-finish-events branch July 15, 2026 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant