@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in
153 lines (130 loc) • 4.93 kB
text/typescript
import { Layers, Object3D } from "three";
import { serializable } from "../engine/engine_serialization_decorator.js";
import { getParam } from "../engine/engine_utils.js";
import { BoxHelperComponent } from "./BoxHelperComponent.js"
import { Behaviour, GameObject } from "./Component.js";
import { EventList } from "./EventList.js";
const debug = getParam("debugspatialtrigger");
const layer1 = new Layers();
const layer2 = new Layers();
function testMask(mask1, mask2) {
layer1.mask = mask1;
layer2.mask = mask2;
return layer1.test(layer2);
}
/**
* @category Interactivity
* @group Components
*/
export class SpatialTriggerReceiver extends Behaviour {
// currently Layers in unity but maybe this should be a string or plane number? Or should it be a bitmask to allow receivers use multiple triggers?
triggerMask: number = 0;
onEnter?: EventList<any>;
onStay?: EventList<any>;
onExit?: EventList<any>;
start() {
if (debug) console.log(this.name, this.triggerMask, this);
}
update(): void {
this.currentIntersected.length = 0;
for (const trigger of SpatialTrigger.triggers) {
if (testMask(trigger.triggerMask, this.triggerMask)) {
if (trigger.test(this.gameObject)) {
this.currentIntersected.push(trigger);
}
}
}
for (let i = this.lastIntersected.length - 1; i >= 0; i--) {
const last = this.lastIntersected[i]
if (this.currentIntersected.indexOf(last) < 0) {
this.onExitTrigger(last);
this.lastIntersected.splice(i, 1);
}
}
for (const cur of this.currentIntersected) {
if (this.lastIntersected.indexOf(cur) < 0)
this.onEnterTrigger(cur);
this.onStayTrigger(cur);
}
this.lastIntersected.length = 0;
this.lastIntersected.push(...this.currentIntersected);
}
currentIntersected: SpatialTrigger[] = [];
lastIntersected: SpatialTrigger[] = [];
onEnterTrigger(trigger: SpatialTrigger): void {
if(debug) console.log("ENTER TRIGGER", this.name, trigger.name, this, trigger);
trigger.raiseOnEnterEvent(this);
this.onEnter?.invoke(this, trigger);
}
onExitTrigger(trigger: SpatialTrigger): void {
if(debug) console.log("EXIT TRIGGER", this.name, trigger.name, this, trigger);
trigger.raiseOnExitEvent(this);
this.onExit?.invoke(this, trigger);
}
onStayTrigger(trigger: SpatialTrigger): void {
trigger.raiseOnStayEvent(this);
this.onStay?.invoke(this, trigger);
}
}
/**
* A trigger that can be used to detect if an object is inside a box.
* @category Interactivity
* @group Components
*/
export class SpatialTrigger extends Behaviour {
static triggers: SpatialTrigger[] = [];
triggerMask?: number;
private boxHelper?: BoxHelperComponent;
start() {
if (debug)
console.log(this.name, this.triggerMask, this);
}
onEnable(): void {
SpatialTrigger.triggers.push(this);
if (!this.boxHelper) {
this.boxHelper = GameObject.addComponent(this.gameObject, BoxHelperComponent);
this.boxHelper?.showHelper(null, debug as boolean);
}
}
onDisable(): void {
SpatialTrigger.triggers.splice(SpatialTrigger.triggers.indexOf(this), 1);
}
test(obj: Object3D): boolean {
if (!this.boxHelper) return false;
return this.boxHelper.isInBox(obj) ?? false;
}
// private args: SpatialTriggerEventArgs = new SpatialTriggerEventArgs();
raiseOnEnterEvent(rec: SpatialTriggerReceiver) {
// this.args.trigger = this;
// this.args.source = rec;
GameObject.foreachComponent(this.gameObject, c => {
if (c === rec) return;
if(c instanceof SpatialTriggerReceiver) {
c.onEnterTrigger(this);
}
}, false);
}
raiseOnStayEvent(rec: SpatialTriggerReceiver) {
// this.args.trigger = this;
// this.args.source = rec;
GameObject.foreachComponent(this.gameObject, c => {
if (c === rec) return;
if(c instanceof SpatialTriggerReceiver) {
c.onStayTrigger(this);
}
}, false);
}
raiseOnExitEvent(rec: SpatialTriggerReceiver) {
GameObject.foreachComponent(this.gameObject, c => {
if (c === rec) return;
if(c instanceof SpatialTriggerReceiver) {
c.onExitTrigger(this);
}
}, false);
}
}