Place compositions in scenes and play them from behaviors (#417)

A composition can now sit in a scene like a container and play there.
Its items become real objects while they run, so behaviors, colliders
and events work on them as on any other object.

- A composition is placeable with its own symbol, transform and render
  layer. The scene view shows its picture at frame 0. A click on that
  picture selects the placement, not a single item.
- CompositionSystem spawns each item as an object under the placement
  when it begins, with the id <placement>~<item>. On start and the
  other events run. The object is destroyed when the item ends.
- The top track lies in front no matter which item started first.
  World.arrange() orders the entity list and the scene objects.
- Clips and driven videos follow the same rules as compositionAt().
  Loop, restart, stop and the game pause work.
- New nodes: Play composition (with restart), Stop composition and the
  event On composition finished. They were planned for step 4 but come
  now, because without autoplay nothing could play otherwise.
- The editor run ignores Play composition and says so in the log. It
  writes into the saved project, and the items would stay behind.
- contentId() moved to instance.ts. The animation system uses it for
  nested ids too.
- Compositions get no "Sort with parent" for now.

Tests: the gate passed with 301 files and 4643 tests, 18 more than
step 1. 13 of them are in tests/core/composition-play.test.ts. The
first gate run failed in event-order.test.ts, which counts the event
nodes and now finds 19. Five counter-checks each turned exactly their
test red. Two order tests stayed green without the fix at first and
were rebuilt. e2e 68 is new and passed with 5 own checks. Its
counter-check without arrange() failed as expected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-12 07:06:33 +02:00
1 parent 73e675835e
commit 0334d2508d
31 files changed
+1035 -33

No files matched your search

+5
View File
@@ -2,6 +2,7 @@ import { type NodeDef, registerNode } from '../NodeRegistry';
import { AddJoint } from './action/AddJoint';
import { Destroy } from './action/Destroy';
import { PauseAnimation, PlayAnimation, StopAnimation } from './action/animationNodes';
import { PlayComposition, StopComposition } from './action/compositionNodes';
import { Emit } from './action/Emit';
import { IsGamePaused, PauseGame, ResumeGame, SetTimeScale, TimeScale } from './action/gameTimeNodes';
import { LoadScene } from './action/LoadScene';
@@ -123,6 +124,7 @@ import { SetItem } from './data/SetItem';
import { SubList } from './data/SubList';
import { TextureSize } from './data/TextureSize';
import { OnAnimationFinished } from './events/OnAnimationFinished';
import { OnCompositionFinished } from './events/OnCompositionFinished';
import { OnBehavior } from './events/OnBehavior';
import { OnClick } from './events/OnClick';
import { OnMouseWheel } from './events/OnMouseWheel';
@@ -183,6 +185,7 @@ export const BUILT_IN_NODES: readonly NodeDef[] = [
OnClick,
OnMouseWheel,
OnAnimationFinished,
OnCompositionFinished,
OnBehavior,
OnStateEntered,
OnStateExited,
@@ -308,6 +311,8 @@ export const BUILT_IN_NODES: readonly NodeDef[] = [
PlayAnimation,
PauseAnimation,
StopAnimation,
PlayComposition,
StopComposition,
PlaySound,
StopSound,
SetSoundFxParameters,