From f4a2d8211ceb760dcecae77ad26ed03e41ba69c4 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Tue, 14 Apr 2026 13:46:46 +0200 Subject: [PATCH] Add actor animation playback and path assignment controls --- src/runtime-three/runtime-host.ts | 60 +++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/runtime-three/runtime-host.ts b/src/runtime-three/runtime-host.ts index 4c21e0b7..a078cabb 100644 --- a/src/runtime-three/runtime-host.ts +++ b/src/runtime-three/runtime-host.ts @@ -1045,6 +1045,15 @@ export class RuntimeHost { case "actorPresence": this.applyActorPresenceControl(state.target.actorId, state.value); return; + case "actorAnimationPlayback": + this.applyActorAnimationPlaybackControl( + state.target, + state.clipName, + state.loop + ); + return; + case "actorPathAssignment": + return; case "modelVisibility": this.applyModelInstanceVisibilityControl(state.target, state.value); return; @@ -1358,6 +1367,45 @@ export class RuntimeHost { } } + private applyActorAnimationPlaybackControl( + target: ActorControlTargetRef, + clipName: string | null, + loop: boolean | undefined + ) { + if (this.runtimeScene !== null) { + for (const npc of this.runtimeScene.npcDefinitions) { + if (npc.actorId !== target.actorId) { + continue; + } + + const nextClipName = clipName; + + if ( + npc.animationClipName === nextClipName && + npc.animationLoop === loop + ) { + continue; + } + + npc.animationClipName = nextClipName; + npc.animationLoop = loop; + } + } + + const npcIds = + this.runtimeScene?.npcDefinitions + .filter((npc) => npc.actorId === target.actorId) + .map((npc) => npc.entityId) ?? []; + + for (const npcId of npcIds) { + if (clipName === null) { + this.applyStopAnimationAction(npcId); + } else { + this.applyPlayAnimationAction(npcId, clipName, loop); + } + } + } + private applySoundVolumeControl( target: SoundEmitterControlTargetRef, volume: number @@ -1449,6 +1497,18 @@ export class RuntimeHost { case "setActorPresence": this.applyActorPresenceControl(effect.target.actorId, effect.active); break; + case "playActorAnimation": + this.applyActorAnimationPlaybackControl( + effect.target, + effect.clipName, + effect.loop + ); + break; + case "followActorPath": + console.warn( + "followActorPath is scheduler-owned in this slice and is ignored when dispatched directly." + ); + break; case "playModelAnimation": this.applyModelAnimationPlaybackControl( effect.target,