Update player position handling in interaction system and tests
This commit is contained in:
@@ -2734,7 +2734,10 @@ export class RuntimeHost {
|
|||||||
this.currentPlayerControllerTelemetry !== null
|
this.currentPlayerControllerTelemetry !== null
|
||||||
) {
|
) {
|
||||||
this.interactionSystem.updatePlayerPosition(
|
this.interactionSystem.updatePlayerPosition(
|
||||||
this.currentPlayerControllerTelemetry.feetPosition,
|
{
|
||||||
|
feetPosition: this.currentPlayerControllerTelemetry.feetPosition,
|
||||||
|
eyePosition: this.currentPlayerControllerTelemetry.eyePosition
|
||||||
|
},
|
||||||
this.runtimeScene,
|
this.runtimeScene,
|
||||||
this.createInteractionDispatcher()
|
this.createInteractionDispatcher()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -255,23 +255,6 @@ function getInteractableTargetRadius(
|
|||||||
return Math.min(DEFAULT_INTERACTABLE_TARGET_RADIUS, interactable.radius);
|
return Math.min(DEFAULT_INTERACTABLE_TARGET_RADIUS, interactable.radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNpcDialogueTargetRadius(npc: RuntimeNpc): number {
|
|
||||||
switch (npc.collider.mode) {
|
|
||||||
case "capsule":
|
|
||||||
return Math.max(
|
|
||||||
DEFAULT_NPC_DIALOGUE_TARGET_RADIUS,
|
|
||||||
npc.collider.radius * 2
|
|
||||||
);
|
|
||||||
case "box":
|
|
||||||
return Math.max(
|
|
||||||
DEFAULT_NPC_DIALOGUE_TARGET_RADIUS,
|
|
||||||
Math.max(npc.collider.size.x, npc.collider.size.z) * 0.75
|
|
||||||
);
|
|
||||||
case "none":
|
|
||||||
return DEFAULT_NPC_DIALOGUE_TARGET_RADIUS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNpcDialoguePrompt(
|
function getNpcDialoguePrompt(
|
||||||
npc: RuntimeNpc,
|
npc: RuntimeNpc,
|
||||||
hasClickLinks: boolean
|
hasClickLinks: boolean
|
||||||
@@ -413,13 +396,21 @@ export class RuntimeInteractionSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updatePlayerPosition(
|
updatePlayerPosition(
|
||||||
feetPosition: Vec3,
|
playerProbe: Vec3 | RuntimePlayerTriggerProbe,
|
||||||
runtimeScene: RuntimeSceneDefinition,
|
runtimeScene: RuntimeSceneDefinition,
|
||||||
dispatcher: RuntimeInteractionDispatcher
|
dispatcher: RuntimeInteractionDispatcher
|
||||||
) {
|
) {
|
||||||
|
const feetPosition = isVec3(playerProbe)
|
||||||
|
? playerProbe
|
||||||
|
: playerProbe.feetPosition;
|
||||||
|
const eyePosition = isVec3(playerProbe)
|
||||||
|
? playerProbe
|
||||||
|
: playerProbe.eyePosition;
|
||||||
|
|
||||||
for (const triggerVolume of runtimeScene.entities.triggerVolumes) {
|
for (const triggerVolume of runtimeScene.entities.triggerVolumes) {
|
||||||
const containsPlayer = isPointInsideTriggerVolume(
|
const containsPlayer = isPlayerInsideTriggerVolume(
|
||||||
feetPosition,
|
feetPosition,
|
||||||
|
eyePosition,
|
||||||
triggerVolume
|
triggerVolume
|
||||||
);
|
);
|
||||||
const wasOccupied = this.occupiedTriggerVolumes.has(
|
const wasOccupied = this.occupiedTriggerVolumes.has(
|
||||||
@@ -562,18 +553,17 @@ export class RuntimeInteractionSystem {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const radius = getNpcDialogueTargetRadius(npc);
|
const bounds = getNpcDialogueTargetBounds(npc);
|
||||||
const distance = distanceBetweenVec3(interactionOrigin, npc.position);
|
const distance = distanceBetweenVec3(interactionOrigin, bounds.center);
|
||||||
|
|
||||||
if (distance > radius) {
|
if (distance > bounds.range) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hitDistance = raySphereHitDistance(
|
const hitDistance = rayAxisAlignedBoxHitDistance(
|
||||||
rayOrigin,
|
rayOrigin,
|
||||||
normalizedViewDirection,
|
normalizedViewDirection,
|
||||||
npc.position,
|
bounds
|
||||||
radius
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (hitDistance === null) {
|
if (hitDistance === null) {
|
||||||
@@ -586,7 +576,7 @@ export class RuntimeInteractionSystem {
|
|||||||
npc.entityId,
|
npc.entityId,
|
||||||
getNpcDialoguePrompt(npc, hasClickLinks),
|
getNpcDialoguePrompt(npc, hasClickLinks),
|
||||||
distance,
|
distance,
|
||||||
radius,
|
bounds.range,
|
||||||
hitDistance
|
hitDistance
|
||||||
);
|
);
|
||||||
bestPrompt = next.prompt;
|
bestPrompt = next.prompt;
|
||||||
|
|||||||
@@ -679,6 +679,61 @@ describe("RuntimeInteractionSystem", () => {
|
|||||||
expect(dispatches).toEqual(["link-trigger-dialogue:dialogue-threshold"]);
|
expect(dispatches).toEqual(["link-trigger-dialogue:dialogue-threshold"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("treats the player body segment as entering a trigger volume, not just the feet point", () => {
|
||||||
|
const runtimeScene = createRuntimeSceneFixture();
|
||||||
|
runtimeScene.entities.triggerVolumes = [
|
||||||
|
{
|
||||||
|
entityId: "entity-trigger-chest-height",
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: 1.2,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
x: 2,
|
||||||
|
y: 1,
|
||||||
|
z: 2
|
||||||
|
},
|
||||||
|
triggerOnEnter: true,
|
||||||
|
triggerOnExit: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
runtimeScene.interactionLinks = [
|
||||||
|
createStartDialogueInteractionLink({
|
||||||
|
id: "link-body-trigger-dialogue",
|
||||||
|
sourceEntityId: "entity-trigger-chest-height",
|
||||||
|
trigger: "enter",
|
||||||
|
dialogueId: "dialogue-threshold"
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
const dispatches: string[] = [];
|
||||||
|
const interactionSystem = new RuntimeInteractionSystem();
|
||||||
|
|
||||||
|
interactionSystem.updatePlayerPosition(
|
||||||
|
{
|
||||||
|
feetPosition: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
|
eyePosition: {
|
||||||
|
x: 0,
|
||||||
|
y: 1.6,
|
||||||
|
z: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
runtimeScene,
|
||||||
|
createDispatcher({
|
||||||
|
startDialogue: (dialogueId, source) => {
|
||||||
|
dispatches.push(`${source?.linkId}:${dialogueId}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(dispatches).toEqual(["link-body-trigger-dialogue:dialogue-threshold"]);
|
||||||
|
});
|
||||||
|
|
||||||
it("resolves direct NPC dialogue prompts and dispatches them through the shared start path", () => {
|
it("resolves direct NPC dialogue prompts and dispatches them through the shared start path", () => {
|
||||||
const runtimeScene = createRuntimeSceneFixture();
|
const runtimeScene = createRuntimeSceneFixture();
|
||||||
runtimeScene.entities.interactables = [];
|
runtimeScene.entities.interactables = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user