Add sound emitter validation and interaction handling

This commit is contained in:
2026-04-02 19:38:16 +02:00
parent 31c1e70542
commit c1f3064e9e
3 changed files with 58 additions and 5 deletions

View File

@@ -807,6 +807,47 @@ function validateInteractionLink(link: InteractionLink, path: string, document:
);
}
break;
case "playSound":
case "stopSound": {
const targetEntity = document.entities[link.action.targetSoundEmitterId];
if (targetEntity === undefined) {
diagnostics.push(
createDiagnostic(
"error",
"missing-sound-emitter-entity",
`Sound emitter entity ${link.action.targetSoundEmitterId} does not exist.`,
`${path}.action.targetSoundEmitterId`
)
);
break;
}
if (targetEntity.kind !== "soundEmitter") {
diagnostics.push(
createDiagnostic(
"error",
"invalid-sound-emitter-kind",
"Sound playback actions must target a Sound Emitter entity.",
`${path}.action.targetSoundEmitterId`
)
);
break;
}
if (targetEntity.audioAssetId === null) {
diagnostics.push(
createDiagnostic(
"error",
"missing-sound-emitter-audio-asset",
"Sound playback actions require a Sound Emitter that references an audio asset.",
`${path}.action.targetSoundEmitterId`
)
);
}
break;
}
default:
diagnostics.push(
createDiagnostic(
@@ -946,7 +987,7 @@ export function validateSceneDocument(document: SceneDocument): SceneDocumentVal
validatePlayerStartEntity(entity, path, diagnostics);
break;
case "soundEmitter":
validateSoundEmitterEntity(entity, path, diagnostics);
validateSoundEmitterEntity(entity, path, document, diagnostics);
break;
case "triggerVolume":
validateTriggerVolumeEntity(entity, path, diagnostics);

View File

@@ -10,6 +10,8 @@ export interface RuntimeInteractionDispatcher {
toggleBrushVisibility(brushId: string, visible: boolean | undefined, link: InteractionLink): void;
playAnimation(instanceId: string, clipName: string, loop: boolean | undefined, link: InteractionLink): void;
stopAnimation(instanceId: string, link: InteractionLink): void;
playSound(soundEmitterId: string, link: InteractionLink): void;
stopSound(soundEmitterId: string, link: InteractionLink): void;
}
export interface RuntimeInteractionPrompt {
@@ -220,6 +222,12 @@ export class RuntimeInteractionSystem {
case "stopAnimation":
dispatcher.stopAnimation(link.action.targetModelInstanceId, link);
break;
case "playSound":
dispatcher.playSound(link.action.targetSoundEmitterId, link);
break;
case "stopSound":
dispatcher.stopSound(link.action.targetSoundEmitterId, link);
break;
}
}
}

View File

@@ -49,8 +49,10 @@ export interface RuntimePlayerStart {
export interface RuntimeSoundEmitter {
entityId: string;
position: Vec3;
radius: number;
gain: number;
audioAssetId: string | null;
volume: number;
refDistance: number;
maxDistance: number;
autoplay: boolean;
loop: boolean;
}
@@ -340,8 +342,10 @@ function buildRuntimeSceneCollections(document: SceneDocument): RuntimeSceneColl
runtimeEntities.soundEmitters.push({
entityId: entity.id,
position: cloneVec3(entity.position),
radius: entity.radius,
gain: entity.gain,
audioAssetId: entity.audioAssetId,
volume: entity.volume,
refDistance: entity.refDistance,
maxDistance: entity.maxDistance,
autoplay: entity.autoplay,
loop: entity.loop
});