Add actor control effects and related functions

This commit is contained in:
2026-04-14 13:40:26 +02:00
parent abca73c7c1
commit 8c634c07da

View File

@@ -213,6 +213,11 @@ export type ControlEffect =
| SetSunLightIntensityControlEffect
| SetSunLightColorControlEffect;
export type ActorControlEffect =
| SetActorPresenceControlEffect
| PlayActorAnimationControlEffect
| FollowActorPathControlEffect;
export interface LightIntensityControlChannelDescriptor {
channel: "light.intensity";
target: LightControlTargetRef;
@@ -480,6 +485,19 @@ export function isControlInteractionTargetKind(
);
}
export function isActorControlEffect(
effect: ControlEffect
): effect is ActorControlEffect {
switch (effect.type) {
case "setActorPresence":
case "playActorAnimation":
case "followActorPath":
return true;
default:
return false;
}
}
export function createActorControlTargetRef(
actorId: string
): ActorControlTargetRef {
@@ -579,6 +597,52 @@ export function createSetActorPresenceControlEffect(options: {
};
}
export function isActorPathProgressMode(
value: unknown
): value is ActorPathProgressMode {
return ACTOR_PATH_PROGRESS_MODES.includes(value as ActorPathProgressMode);
}
export function createPlayActorAnimationControlEffect(options: {
target: ActorControlTargetRef;
clipName: string;
loop?: boolean;
}): PlayActorAnimationControlEffect {
assertNonEmptyString(options.clipName, "Control actor animation clip name");
return {
type: "playActorAnimation",
target: cloneControlTargetRef(options.target) as ActorControlTargetRef,
clipName: options.clipName,
loop: options.loop
};
}
export function createFollowActorPathControlEffect(options: {
target: ActorControlTargetRef;
pathId: string;
speed: number;
loop?: boolean;
progressMode?: ActorPathProgressMode;
}): FollowActorPathControlEffect {
assertNonEmptyString(options.pathId, "Control actor path id");
if (!Number.isFinite(options.speed) || options.speed <= 0) {
throw new Error(
"Control actor path speed must be a finite number greater than zero."
);
}
return {
type: "followActorPath",
target: cloneControlTargetRef(options.target) as ActorControlTargetRef,
pathId: options.pathId,
speed: options.speed,
loop: options.loop ?? false,
progressMode: options.progressMode ?? "deriveFromTime"
};
}
export function createPlayModelAnimationControlEffect(options: {
target: ModelInstanceControlTargetRef;
clipName: string;
@@ -903,6 +967,66 @@ export function createResolvedActorPresenceState(options: {
};
}
export function createResolvedActorAnimationPlaybackState(options: {
target: ActorControlTargetRef;
clipName: string | null;
loop: boolean | undefined;
source: RuntimeResolvedControlSource;
}): RuntimeResolvedActorAnimationPlaybackState {
if (options.clipName !== null) {
assertNonEmptyString(options.clipName, "Resolved control actor animation clip");
}
return {
type: "actorAnimationPlayback",
target: cloneControlTargetRef(options.target) as ActorControlTargetRef,
clipName: options.clipName,
loop: options.loop,
source: cloneResolvedControlSource(options.source)
};
}
export function createResolvedActorPathAssignmentState(options: {
target: ActorControlTargetRef;
pathId: string | null;
speed: number | null;
loop: boolean;
progressMode: ActorPathProgressMode | null;
source: RuntimeResolvedControlSource;
}): RuntimeResolvedActorPathAssignmentState {
if (options.pathId !== null) {
assertNonEmptyString(options.pathId, "Resolved control actor path id");
}
if (
options.speed !== null &&
(!Number.isFinite(options.speed) || options.speed <= 0)
) {
throw new Error(
"Resolved control actor path speed must be a finite number greater than zero."
);
}
if (
(options.pathId === null) !== (options.speed === null) ||
(options.pathId === null) !== (options.progressMode === null)
) {
throw new Error(
"Resolved actor path assignments must either define path, speed, and progress mode together or clear all of them."
);
}
return {
type: "actorPathAssignment",
target: cloneControlTargetRef(options.target) as ActorControlTargetRef,
pathId: options.pathId,
speed: options.speed,
loop: options.loop,
progressMode: options.progressMode,
source: cloneResolvedControlSource(options.source)
};
}
export function createResolvedInteractionEnabledState(options: {
target: InteractionControlTargetRef;
value: boolean;