This commit is contained in:
Vasyl Kazakov
2026-05-29 18:01:05 +03:00
parent 2633be60a1
commit 6363396f84
20 changed files with 978 additions and 81 deletions
+108
View File
@@ -0,0 +1,108 @@
import {
EasyEvent,
Physics_internal,
UpdateController,
} from "@24tools/playable_template";
import { Object3D } from "three";
import { Body, Box } from "cannon-es";
import { PhysicsBody, PhysicsLayer } from "../PhysicsC";
const PLAYER_RADIUS = 0.3;
export type TriggerEventPayload = {
lootableObject: Object3D;
triggerObject: Object3D;
playerBody: Body;
};
type TriggerRecord = {
lootableObject: Object3D;
triggerObject: Object3D;
radius: number;
position: Body["position"];
physicsBody: PhysicsBody;
};
export class PhysicsTriggerC {
private static inited = false;
private static triggers: TriggerRecord[] = [];
private static activeTriggers = new Set<TriggerRecord>();
static onTriggerEnter = new EasyEvent<TriggerEventPayload>();
static onTriggerExit = new EasyEvent<TriggerEventPayload>();
static init() {
if (this.inited) return;
this.inited = true;
UpdateController.Instance.onUpdate.addDelegate(() => this.update());
}
static register(triggerObject: Object3D, lootableObject: Object3D) {
const physicsBody = new PhysicsBody(
triggerObject,
true,
0,
PhysicsLayer.Trigger,
PhysicsLayer.Player,
);
const body = physicsBody.getPhysicsBody();
body.allowSleep = false;
const shape = body.shapes[0] as Box;
const halfExtents = shape.halfExtents;
this.triggers.push({
lootableObject,
triggerObject,
radius: Math.max(halfExtents.x, halfExtents.y, halfExtents.z),
position: body.position,
physicsBody,
});
}
static unregister(lootableObject: Object3D) {
this.triggers = this.triggers.filter((record) => {
if (record.lootableObject !== lootableObject) return true;
record.physicsBody.destroy();
this.activeTriggers.delete(record);
return false;
});
}
private static update() {
const playerBody = Physics_internal.physicsWorld?.bodies.find(
(body) => body.collisionFilterGroup === PhysicsLayer.Player,
);
if (!playerBody) return;
for (const trigger of this.triggers) {
const dx = playerBody.position.x - trigger.position.x;
const dy = playerBody.position.y - trigger.position.y;
const dz = playerBody.position.z - trigger.position.z;
const distanceSq = dx * dx + dy * dy + dz * dz;
const isInside = distanceSq <= (trigger.radius + PLAYER_RADIUS) ** 2;
const wasInside = this.activeTriggers.has(trigger);
if (isInside === wasInside) continue;
const payload: TriggerEventPayload = {
lootableObject: trigger.lootableObject,
triggerObject: trigger.triggerObject,
playerBody,
};
if (isInside) {
this.activeTriggers.add(trigger);
this.onTriggerEnter.Invoke(payload);
console.log("[Gather Enter]", trigger.lootableObject.name);
} else {
this.activeTriggers.delete(trigger);
this.onTriggerExit.Invoke(payload);
console.log("[Gather Exit]", trigger.lootableObject.name);
}
}
}
}