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
@@ -0,0 +1,40 @@
|
||||
import { playComposition, stopComposition } from '../../../model/composition';
|
||||
import type { NodeDef } from '../../NodeRegistry';
|
||||
import { asBoolean, asEntity } from '../../values';
|
||||
import { bool, exec, obj, withDefault } from '../shared';
|
||||
|
||||
export const PlayComposition: NodeDef = {
|
||||
type: 'PlayComposition',
|
||||
label: 'Play composition',
|
||||
category: 'action',
|
||||
description:
|
||||
'Plays a composition placed in the scene from its first frame: each item becomes an object below ' +
|
||||
'it when the item begins – its behaviors start then – and is destroyed when it ends. One already ' +
|
||||
'playing plays on; Restart starts it over. Without Loop it ends after its last item, and On ' +
|
||||
'composition finished hears that. The editor run plays none and says so in the log.',
|
||||
inputs: [exec(), obj('object'), withDefault(bool('restart'), false)],
|
||||
outputs: [exec()],
|
||||
params: [],
|
||||
run: (ctx, inputs) => {
|
||||
const target = asEntity(inputs.object);
|
||||
if (target) playComposition(target, asBoolean(inputs.restart), ctx.self);
|
||||
return { next: 'exec' };
|
||||
},
|
||||
};
|
||||
|
||||
export const StopComposition: NodeDef = {
|
||||
type: 'StopComposition',
|
||||
label: 'Stop composition',
|
||||
category: 'action',
|
||||
description:
|
||||
'Stops a playing composition: the objects of its items are destroyed on the next frame. It does ' +
|
||||
'not count as finished, and Play composition starts it from its first frame again.',
|
||||
inputs: [exec(), obj('object')],
|
||||
outputs: [exec()],
|
||||
params: [],
|
||||
run: (_ctx, inputs) => {
|
||||
const target = asEntity(inputs.object);
|
||||
if (target) stopComposition(target);
|
||||
return { next: 'exec' };
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { textOf } from '../../../model/Properties';
|
||||
import type { NodeDef } from '../../NodeRegistry';
|
||||
import { hiddenOrder, orderParam } from '../../eventOrder';
|
||||
import { exec, obj } from '../shared';
|
||||
|
||||
export const OnCompositionFinished: NodeDef = {
|
||||
type: 'OnCompositionFinished',
|
||||
label: 'On composition finished',
|
||||
category: 'event',
|
||||
description:
|
||||
'Runs once when a composition placed at the owner, or below it, plays past its last item – and when ' +
|
||||
'one this behavior played itself does, wherever it stands. A looping one never finishes, and Stop ' +
|
||||
'composition does not count. Leave Composition at None to hear every composition.',
|
||||
inputs: [],
|
||||
outputs: [exec(), obj('object')],
|
||||
params: [
|
||||
{ key: 'composition', label: 'Composition', kind: 'asset', default: '', assetTypes: ['composition'] },
|
||||
orderParam,
|
||||
],
|
||||
hiddenParams: (params) => [
|
||||
...(textOf(params.composition ?? '') ? [] : ['composition']),
|
||||
...hiddenOrder(params),
|
||||
],
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user