Compiled scenes
Turning a frame number into state, finding actors in it, caching it and reflecting it for editors.
A compiled scene is the record a scene script wrote: what
its actors did and when. scene holds what the script returned: the actor objects it
authored with, which after the build only identify their actors. A write or a lifecycle call
on one throws, and its build owns it, so a renderer binds it only against that build
(actor.<tag> of={…}). at(frame) replays the record to the frame you ask for and returns a
FrameState, whose state.scene holds snapshots of the same actors at that frame: their
values, but no handle.
import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>Declare a scene. Nothing compiles until a host builds it:
`scene.build({ viewport, fps, inputs })` runs the script as
`(ctx, inputs, viewport, layers)` and returns the compiled scene.
`ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn`
places it in a declared one. `ctx.stage` stages another scene on this
scene's clock, and a staged scene may stage again.createScene, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor, function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it.
Without an initial value the signal is required: `ctx.spawn` must supply it.signal } from "@motionactor/core";
export class class TitleTitle extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor {
static Title.type: stringtype = "title";
Title.text: ActorFieldSignal<string>text = signal<string>(initial: string): ActorFieldSignal<string> (+1 overload)A timeline value on an actor, and an input to every spawn of it.
Without an initial value the signal is required: `ctx.spawn` must supply it.signal("");
}
export const const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled = createScene<{
title: Title;
}, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
title: Title;
}>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it:
`scene.build({ viewport, fps, inputs })` runs the script as
`(ctx, inputs, viewport, layers)` and returns the compiled scene.
`ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn`
places it in a declared one. `ctx.stage` stages another scene on this
scene's clock, and a staged scene may stage again.createScene(function* (ctx: SceneContextctx) {
const const title: Titletitle = ctx: SceneContextctx.CompileContext.spawn<Title>(definition: ActorClass<Title>, overrides?: {
measuredBox?: undefined;
text?: SignalInputValue<string> | undefined;
appearance?: {
opacity?: SignalInputValue<number> | undefined;
} | undefined;
id?: undefined;
layout?: {
x?: SignalInputValue<LayoutValue> | undefined;
y?: SignalInputValue<LayoutValue> | undefined;
width?: Readonly<{
kind: "layout.percent";
value: number;
}> | SignalInputValue<number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>> | undefined;
height?: Readonly<...> | ... 1 more ... | undefined;
} | undefined;
... 18 more ...;
parent?: undefined;
} | undefined, options?: {
...;
} | undefined): Title (+1 overload)
spawn(
class TitleTitle,
{ text?: SignalInputValue<string> | undefinedtext: "Launch day", appearance?: {
opacity?: SignalInputValue<number> | undefined;
} | undefined
appearance: { opacity?: SignalInputValue<number> | undefinedopacity: 0 } },
{ id?: string | undefinedid: "title" },
);
yield* const title: Titletitle.LayoutActor<{}>.appearance: {
opacity: TypedFieldSignal<number, number>;
} & SignalGroupDefinition
appearance.opacity: TypedFieldSignal<number, number>opacity.function tween(target: number, options: number | SignalTweenOptions<number>): Generator<Frame, void, unknown>Tween to target over authored seconds — must yield*tween(1, 0.5);
yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(1);
return { title: Titletitle };
}).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, { ...; }>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });
const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.fps: numberfps; // 30
const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.totalDuration: numbertotalDuration; // 45
const const state: FrameState<{
title: Title;
}>
state = const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
title: Title;
}>
at(30);
const state: FrameState<{
title: Title;
}>
state.FrameState<{ ...; }>.scene: {
title: ResolvedActorOfInstance<Title> | undefined;
}
scene.title: ResolvedActorOfInstance<Title> | undefinedtitle?.text: string | undefinedtext; // "Launch day"
const state: FrameState<{
title: Title;
}>
state.FrameState<{ ...; }>.camera: Camera2DState2D scene camera. Identity when no spawned actor's class declares `SCENE_CAMERA`.camera.Camera2DState.zoom: numberzoom; // 1
CompiledScene |
|
|---|---|
fps |
Frames per second the scene compiled at |
totalDuration |
Length in frames |
at(frame, options?) |
The FrameState at that frame |
scene |
What the script returned, as the build’s actor handles: page markup binds one (page bindings) |
layers |
One handle per declared layer, typed by its backend: page markup addresses one with InLayer |
layersOf(scene) |
The handles on a staged scene’s layers, by the definition staged, at any depth |
placements |
Every staged scene at any depth, stager first: path, parentPath, depth, startFrame, endFrame |
reflect(frame?, options?) |
The frame’s actors described for an editor |
events, operations |
The raw record, for tools |
cells |
The build’s publications, for a host: subscribe(fn) and version() move when one changes, resetRun() starts a new run |
[Symbol.dispose]() |
Releases the build; see below |
A frame past the end resolves to the final state, so a host that overshoots shows the last frame. A frame before 0 resolves with no actors.
Frame state
FrameState |
|
|---|---|
frame |
The frame it resolves |
scene |
The scene’s exports, each actor resolved |
actors |
Every live actor, as a Map from id |
layers |
The frame’s layers in paint order |
camera |
centerX, centerY, zoom, rotation, originX, originY |
An actor that has not spawned yet, or has despawned, is absent from actors and resolves to
undefined in scene.
camera comes from the scene’s camera actor, such as Camera2D, and
is identity when there is none.
Looking up actors
The scene’s exports are the typed way in. For an actor the scene did not return, look it up by class. The class carries the type, so the result needs no cast, and a subclass instance matches too:
import { const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled, class TitleTitle } from "./title.scene";
const const state: FrameState<{
title: Title;
}>
state = const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ title: Title; }, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
title: Title;
}>
at(30);
const state: FrameState<{
title: Title;
}>
state.FrameState<{ title: Title; }>.findActor<typeof Title>(blueprint: typeof Title): (Omit<ResolvedActor, "signals" | "channels"> & {
text: string;
appearance: {
opacity: number;
};
measuredBox: Rect | undefined;
layout: {
x: LayoutValue;
y: LayoutValue;
width: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
height: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
};
transform: {
scale: number;
rotation: number;
};
shape: {
radius: number;
};
compositing: {
blend: "source-over" | "multiply" | ... 14 more ... | "plus-lighter";
isolate: boolean;
clip: number | null;
};
flexItem: {
...;
};
gridItem: {
...;
};
transitionState: {
...;
};
} & {
...;
}) | undefined
findActor(class TitleTitle)?.text: string | undefinedtext; // first match
const state: FrameState<{
title: Title;
}>
state.FrameState<{ title: Title; }>.findActors<typeof Title>(blueprint: typeof Title): (Omit<ResolvedActor, "signals" | "channels"> & {
text: string;
appearance: {
opacity: number;
};
measuredBox: Rect | undefined;
layout: {
x: LayoutValue;
y: LayoutValue;
width: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
height: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
};
transform: {
scale: number;
rotation: number;
};
shape: {
radius: number;
};
compositing: {
blend: "source-over" | "multiply" | ... 14 more ... | "plus-lighter";
isolate: boolean;
clip: number | null;
};
flexItem: {
...;
};
gridItem: {
...;
};
transitionState: {
...;
};
} & {
...;
})[]
findActors(class TitleTitle); // every match
const state: FrameState<{
title: Title;
}>
state.FrameState<{ title: Title; }>.getActor<typeof Title>(id: string, blueprint: typeof Title): (Omit<ResolvedActor, "signals" | "channels"> & {
text: string;
appearance: {
opacity: number;
};
measuredBox: Rect | undefined;
layout: {
x: LayoutValue;
y: LayoutValue;
width: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
height: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
};
transform: {
scale: number;
rotation: number;
};
shape: {
radius: number;
};
compositing: {
blend: "source-over" | ... 15 more ... | "plus-lighter";
isolate: boolean;
clip: number | null;
};
flexItem: {
...;
};
gridItem: {
...;
};
transitionState: {
...;
};
} & {
...;
}) | undefined
getActor("title", class TitleTitle); // by id, if it is a Title
const state: FrameState<{
title: Title;
}>
state.FrameState<{ title: Title; }>.actor(id: string): ResolvedActor | undefinedactor("title"); // by id, untyped
const state: FrameState<{
title: Title;
}>
state.FrameState<{ title: Title; }>.findActorByType(type: string): ResolvedActor | undefinedFirst actor whose class lineage includes `type` (its `static type` chain,
computed from `classCtor`). Matches subclasses, as `findActor` does, for
a caller that has the type name and not the class.findActorByType("title"); // by static type, untyped
findActorByType matches the static type of the class and its parents. It is for code that
has a type name but not the class, such as an inspector.
Resolved actors
Every lookup and export resolves to an actor’s state at the frame. Its fields appear under their own names, typed by the class, beside these:
| Key | Holds |
|---|---|
id, type |
The actor’s id and its class’s static type |
parentId, slotName |
Its host and the slot it is in; null for a root actor |
slotChildren |
The ids in each of its slots, in hosted order |
localLayout, worldLayout |
Its box relative to its parent and to the stage |
compositing |
Opacity, blend mode, isolation and clip, ready for a backend |
effects |
The effects active on it |
layer |
The id of the layer it paints in: its key, or key#2 when the build declares the key twice; null for the default |
spawnFrame, despawnFrame |
When it entered and left |
signals |
Every field’s value by name, channels included |
channels |
A handle per channel: its value when called, write, unpublish and demand |
Values from the host
Percentages resolve against the viewport the scene was built for; a new size is a new build. A value the host measures goes into a channel, written through the resolved actor’s handle:
import {
function channel<TValue>(options: {
readonly default: TValue;
readonly record?: boolean;
readonly equals?: (previous: TValue, next: TValue) => boolean;
}): ChannelDefinition<TValue> & {
readonly hasDefault: true;
readonly default: TValue;
} (+1 overload)
A channel is a writable, scope-aware cell carrying an actor-channel brand.
Declared as an actor field it lives in the spawning build's store (keyed
`${actorId}:${name}` by the spawn walk, like a signal); declared free (in
`buildScene`, a script, or module top) it keys by its scope, in its build's
store or the module's, and is shared by reference. Either way resolution
reads it live.
The static return type stays `ChannelDefinition<TValue>` so the
`isChannelDefinition` predicate is unchanged; at runtime the handle is a
live cell.channel,
function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>Declare a scene. Nothing compiles until a host builds it:
`scene.build({ viewport, fps, inputs })` runs the script as
`(ctx, inputs, viewport, layers)` and returns the compiled scene.
`ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn`
places it in a declared one. `ctx.stage` stages another scene on this
scene's clock, and a staged scene may stage again.createScene,
function derived<TThis, T>(fn: (this: TThis) => T): DerivedDefinition<T, TThis>derived,
class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor,
function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it.
Without an initial value the signal is required: `ctx.spawn` must supply it.signal,
} from "@motionactor/core";
class class LabelLabel extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor {
Label.text: ActorFieldSignal<string>text = signal<string>(initial: string): ActorFieldSignal<string> (+1 overload)A timeline value on an actor, and an input to every spawn of it.
Without an initial value the signal is required: `ctx.spawn` must supply it.signal("");
Label.available: ChannelDefinition<number> & {
readonly hasDefault: true;
readonly default: number;
}
available = channel<number>(options: {
readonly default: number;
readonly record?: boolean;
readonly equals?: ((previous: number, next: number) => boolean) | undefined;
}): ChannelDefinition<number> & {
readonly hasDefault: true;
readonly default: number;
} (+1 overload)
A channel is a writable, scope-aware cell carrying an actor-channel brand.
Declared as an actor field it lives in the spawning build's store (keyed
`${actorId}:${name}` by the spawn walk, like a signal); declared free (in
`buildScene`, a script, or module top) it keys by its scope, in its build's
store or the module's, and is shared by reference. Either way resolution
reads it live.
The static return type stays `ChannelDefinition<TValue>` so the
`isChannelDefinition` predicate is unchanged; at runtime the handle is a
live cell.channel({ default: numberdefault: 0 });
Label.fits: DerivedDefinition<boolean, unknown>fits = derived<unknown, boolean>(fn: (this: unknown) => boolean): DerivedDefinition<boolean, unknown>derived(() => this.Label.text: () => stringtext().String.length: numberReturns the length of a String object.length * 30 <= this.Label.available: () => numberavailable());
}
const const compiled: CompiledScene<{
label: Label;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled = createScene<{
label: Label;
}, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
label: Label;
}>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it:
`scene.build({ viewport, fps, inputs })` runs the script as
`(ctx, inputs, viewport, layers)` and returns the compiled scene.
`ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn`
places it in a declared one. `ctx.stage` stages another scene on this
scene's clock, and a staged scene may stage again.createScene(function* (ctx: SceneContextctx) {
const const label: Labellabel = ctx: SceneContextctx.CompileContext.spawn<Label>(definition: ActorClass<Label>, overrides?: {
measuredBox?: undefined;
text?: SignalInputValue<string> | undefined;
id?: undefined;
available?: undefined;
fits?: undefined;
layout?: {
x?: SignalInputValue<LayoutValue> | undefined;
y?: SignalInputValue<LayoutValue> | undefined;
width?: Readonly<{
kind: "layout.percent";
value: number;
}> | SignalInputValue<number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>> | undefined;
height?: Readonly<...> | ... 1 more ... | undefined;
} | undefined;
... 19 more ...;
parent?: undefined;
} | undefined, options?: {
...;
} | undefined): Label (+1 overload)
spawn(class LabelLabel, { text?: SignalInputValue<string> | undefinedtext: "Launch day" }, { id?: string | undefinedid: "label" });
yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(1);
return { label: Labellabel };
}).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, { ...; }>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<{
label: Label;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });
const compiled: CompiledScene<{
label: Label;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
label: Label;
}>
at(0).FrameState<{ ...; }>.scene: {
label: ResolvedActorOfInstance<Label> | undefined;
}
scene.label: ResolvedActorOfInstance<Label> | undefinedlabel?.fits: boolean | undefinedfits; // false
const compiled: CompiledScene<{
label: Label;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
label: Label;
}>
at(0).FrameState<{ ...; }>.scene: {
label: ResolvedActorOfInstance<Label> | undefined;
}
scene.label: ResolvedActorOfInstance<Label> | undefinedlabel?.channels: ActorClassChannels<Omit<Label, keyof Actor<any>>>channels.available: ResolvedChannelHandle<number>available.ResolvedChannelHandle<number>.write(value: number, frame?: number): voidPublish `value`. `frame` is the frame the producer's scene host committed;
a `record: true` channel files the value under it, so a later seek reads
the value observed at that frame. Without one the write is live only.write(600);
const compiled: CompiledScene<{
label: Label;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
label: Label;
}>
at(0).FrameState<{ ...; }>.scene: {
label: ResolvedActorOfInstance<Label> | undefined;
}
scene.label: ResolvedActorOfInstance<Label> | undefinedlabel?.fits: boolean | undefinedfits; // true
The publication applies to every later call until it is replaced or removed.
Caching
at keeps each actor’s resolved values and revisits only the actors whose values can have
changed: a timeline they read moves, a channel they read is published, or an effect on them is
active. An actor that holds is the same object in the next frame, and a repeat of the last
frame with nothing due is the same FrameState. frame.resolution counts the actors visited.
at(frame, { cache: false }) resolves afresh and keeps nothing.
import { const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled } from "./title.scene";
const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ title: Title; }, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
title: Title;
}>
at(10) === const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ title: Title; }, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
title: Title;
}>
at(10); // true
const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ title: Title; }, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
title: Title;
}>
at(11).FrameState<{ title: Title; }>.resolution: FrameResolutionHow this frame was resolved: the actors visited, of the live ones.resolution; // { visited: 1, actors: 1 }
const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ title: Title; }, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
title: Title;
}>
at(10, { FrameResolutionOptions.cache?: false | undefined`false` resolves the frame afresh: nothing is reused from earlier calls and
nothing is kept for later ones. The frame equals a cached call's.cache: false }) === const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ title: Title; }, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
title: Title;
}>
at(10); // false
Reflection
reflect(frame) describes the frame’s actors for an editor that holds no classes: each one’s
fields with their current values, its groups and its commands.
import { const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled } from "./title.scene";
const const title: ReflectedActor | nulltitle = const compiled: CompiledScene<{
title: Title;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ title: Title; }, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>.reflect(frame?: Frame, options?: FrameResolutionOptions): SceneReflectionreflect(15).SceneReflection.getActor(id: string): ReflectedActor | nullgetActor("title");
const title: ReflectedActor | nulltitle?.ReflectedActor.groups: ReflectedGroup[]groups.Array<ReflectedGroup>.map<U>(callbackfn: (value: ReflectedGroup, index: number, array: ReflectedGroup[]) => U, thisArg?: any): U[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((group: ReflectedGroupgroup) => group: ReflectedGroupgroup.ReflectedGroup.key: stringkey);
// ["appearance", "compositing", "flexItem", "gridItem",
// "layout", "transform", "transitionState"]
const title: ReflectedActor | nulltitle?.ReflectedActor.fields: ReflectedField[]fields.Array<ReflectedField>.find<S>(predicate: (value: ReflectedField, index: number, obj: ReflectedField[]) => value is S, thisArg?: any): S | undefined (+1 overload)Returns the value of the first element in the array where predicate is true, and undefined
otherwise.find((field: ReflectedFieldfield) => field: ReflectedFieldfield.ReflectedField.path: stringpath === "text")?.ReflectedField.currentValue: unknowncurrentValue;
// "Launch day"
reflectActor(Class, overrides) reflects one actor without a scene, and
reflectBlueprint(Class) reflects a class before anything spawns it. Commands and their
parameters come from @command.
Releasing a build
A compiled scene owns its cells: their values, publications and recorded frames. Disposing it releases them:
import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>Declare a scene. Nothing compiles until a host builds it:
`scene.build({ viewport, fps, inputs })` runs the script as
`(ctx, inputs, viewport, layers)` and returns the compiled scene.
`ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn`
places it in a declared one. `ctx.stage` stages another scene on this
scene's clock, and a staged scene may stage again.createScene, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor } from "@motionactor/core";
class class CardCard extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor {}
{
using using compiled: CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled = createScene<{
card: Card;
}, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
card: Card;
}>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it:
`scene.build({ viewport, fps, inputs })` runs the script as
`(ctx, inputs, viewport, layers)` and returns the compiled scene.
`ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn`
places it in a declared one. `ctx.stage` stages another scene on this
scene's clock, and a staged scene may stage again.createScene(function* (ctx: SceneContextctx) {
const const card: Cardcard = ctx: SceneContextctx.CompileContext.spawn<Card>(definition: ActorClass<Card>, overrides?: {
measuredBox?: undefined;
layout?: {
x?: SignalInputValue<LayoutValue> | undefined;
y?: SignalInputValue<LayoutValue> | undefined;
width?: Readonly<{
kind: "layout.percent";
value: number;
}> | SignalInputValue<number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>> | undefined;
height?: Readonly<{
kind: "layout.percent";
value: number;
}> | SignalInputValue<number | `${number}%` | Readonly<...>> | undefined;
} | undefined;
... 20 more ...;
parent?: undefined;
} | undefined, options?: {
...;
} | undefined): Card (+1 overload)
spawn(class CardCard);
yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(1);
return { card: Cardcard };
}).SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, { ...; }>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
build({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });
using compiled: CompiledScene<{
card: Card;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled.CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
card: Card;
}>
at(0).FrameState<{ ...; }>.scene: {
card: ResolvedActorOfInstance<Card> | undefined;
}
scene.card: ResolvedActorOfInstance<Card> | undefinedcard?.layout: {
x: LayoutValue;
y: LayoutValue;
width: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
height: number | `${number}%` | Readonly<{
kind: "layout.percent";
value: number;
}>;
}
layout.x: LayoutValue | undefinedx; // 0
} // released here
After that, at throws This compiled scene was disposed: build it again to resolve its frames., and a write through any of its handles throws. Outside a block, call
compiled[Symbol.dispose](). A composition build is disposed the
same way.