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:
1 parent
73e675835e
commit
0334d2508d
31 files changed
+1035
-33
No files matched your search
@@ -5,6 +5,7 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { requireNodeDef } from '../../src/core/commands/require';
|
||||
import { BUILT_IN_NODES } from '../../src/core/graph/nodes';
|
||||
import { playComposition } from '../../src/core/model/composition';
|
||||
import {
|
||||
createEmptyGraph,
|
||||
type DeclaredPort,
|
||||
@@ -107,6 +108,8 @@ const nestedMover = {
|
||||
const emitTarget = fakeEntity('Sparks');
|
||||
/** Their own entity each: the animation nodes leave players on the object they are handed. */
|
||||
const dancer = fakeEntity('Dancer');
|
||||
/** A placed composition for Play and Stop composition (#417). */
|
||||
const director = fakeEntity('Director');
|
||||
/** An object whose once-reset "swing" has run out and blends back over half a second (#233). */
|
||||
const swinging = {
|
||||
...fakeEntity('Fighter'),
|
||||
@@ -1976,6 +1979,34 @@ const CASES: Record<string, Case[]> = {
|
||||
after: () => expect(dancer.animations).toEqual([]),
|
||||
},
|
||||
],
|
||||
PlayComposition: [
|
||||
{
|
||||
name: 'no object is a no-op that still continues the flow',
|
||||
inputs: { object: null, restart: false },
|
||||
next: 'exec',
|
||||
},
|
||||
{
|
||||
name: 'starts a player on the object, and a second play leaves it running',
|
||||
inputs: { object: director, restart: false },
|
||||
next: 'exec',
|
||||
after: () => {
|
||||
expect(director.composition).toMatchObject({ time: 0, shown: false, stopped: false });
|
||||
const run = director.composition!.run;
|
||||
playComposition(director);
|
||||
expect(director.composition!.run).toBe(run);
|
||||
playComposition(director, true);
|
||||
expect(director.composition!.run).toBeGreaterThan(run);
|
||||
},
|
||||
},
|
||||
],
|
||||
StopComposition: [
|
||||
{
|
||||
name: 'marks the player stopped for the next tick to take its items away',
|
||||
inputs: { object: director },
|
||||
next: 'exec',
|
||||
after: () => expect(director.composition?.stopped).toBe(true),
|
||||
},
|
||||
],
|
||||
PlaySound: [
|
||||
{
|
||||
name: 'no sound is a no-op that still continues the flow',
|
||||
@@ -2506,6 +2537,7 @@ const EVENT_NODES: Record<string, string> = {
|
||||
OnClick: 'nodes.test.ts',
|
||||
OnMouseWheel: 'clip-areas.test.ts',
|
||||
OnAnimationFinished: 'animation.test.ts',
|
||||
OnCompositionFinished: 'composition-play.test.ts',
|
||||
OnStateEntered: 'state-machine.test.ts',
|
||||
OnStateExited: 'state-machine.test.ts',
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user