-
Marshall Quander authoredMarshall Quander authored
personal-space-bubble.js 5.90 KiB
const invaderPos = new AFRAME.THREE.Vector3();
const bubblePos = new AFRAME.THREE.Vector3();
/**
* Iterates through bubbles and invaders on every tick and sets invader state accordingly.
* testing multiline things
* @namespace avatar/personal-space-bubble
* @system personal-space-bubble
*/
AFRAME.registerSystem("personal-space-bubble", {
schema: {
debug: { default: false },
enabled: { default: true }
},
init() {
this.invaders = [];
this.bubbles = [];
this.el.addEventListener("action_space_bubble", () => {
this.el.setAttribute("personal-space-bubble", { enabled: !this.data.enabled });
});
},
registerBubble(bubble) {
this.bubbles.push(bubble);
},
unregisterBubble(bubble) {
const index = this.bubbles.indexOf(bubble);
if (index !== -1) {
this.bubbles.splice(index, 1);
}
},
registerInvader(invader) {
NAF.utils.getNetworkedEntity(invader.el).then(networkedEl => {
const owner = NAF.utils.getNetworkOwner(networkedEl);
if (owner !== NAF.clientId) {
this.invaders.push(invader);
}
});
},
unregisterInvader(invader) {
const index = this.invaders.indexOf(invader);
if (index !== -1) {
this.invaders.splice(index, 1);
}
},
update() {
for (let i = 0; i < this.bubbles.length; i++) {
this.bubbles[i].updateDebug();
}
for (let i = 0; i < this.invaders.length; i++) {
this.invaders[i].updateDebug();
if (!this.data.enabled) {
this.invaders[i].setInvading(false);
}
}
if (this.data.enabled) {
this.el.addState("spacebubble");
} else {
this.el.removeState("spacebubble");
}
},
tick() {
if (!this.data.enabled) return;
// precondition for this stuff -- the bubbles and invaders need updated world matrices.
// right now this is satisfied because we update the world matrices in the character controller
for (let i = 0; i < this.invaders.length; i++) {
this.invaders[i].setInvading(false);
}
// Loop through all of the space bubbles (usually one)
for (let i = 0; i < this.bubbles.length; i++) {
const bubble = this.bubbles[i];
bubblePos.setFromMatrixPosition(bubble.el.object3D.matrixWorld);
// Hide the invader if inside the bubble
for (let j = 0; j < this.invaders.length; j++) {
const invader = this.invaders[j];
invaderPos.setFromMatrixPosition(invader.el.object3D.matrixWorld);
const distanceSquared = bubblePos.distanceToSquared(invaderPos);
const radiusSum = bubble.data.radius + invader.data.radius;
if (distanceSquared < radiusSum * radiusSum) {
invader.setInvading(true);
}
}
}
}
});
function createSphereGizmo(radius) {
const geometry = new THREE.SphereBufferGeometry(radius, 10, 10);
const wireframe = new THREE.WireframeGeometry(geometry);
const line = new THREE.LineSegments(wireframe);
line.material.opacity = 0.5;
line.material.transparent = true;
return line;
}
// TODO: we need to come up with a more generic way of doing this as this is very specific to our avatars.
/**
* Specifies a mesh associated with an invader.
* @namespace avatar/personal-space-bubble
* @component space-invader-mesh
*/
AFRAME.registerComponent("space-invader-mesh", {
schema: {
meshName: { type: "string" }
},
update() {
this.targetMesh = this.el.object3D.getObjectByName(this.data.meshName);
}
});
function findInvaderMesh(entity) {
while (entity && !(entity.components && entity.components["space-invader-mesh"])) {
entity = entity.parentNode;
}
return entity && entity.components["space-invader-mesh"].targetMesh;
}
const DEBUG_OBJ = "psb-debug";
/**
* Represents an entity that can invade a personal space bubble
* @namespace avatar/personal-space-bubble
* @component personal-space-invader
*/
AFRAME.registerComponent("personal-space-invader", {
schema: {
radius: { type: "number", default: 0.1 },
useMaterial: { default: false },
debug: { default: false },
invadingOpacity: { default: 0.3 }
},
init() {
const system = this.el.sceneEl.systems["personal-space-bubble"];
system.registerInvader(this);
if (this.data.useMaterial) {
const mesh = findInvaderMesh(this.el);
if (mesh) {
this.targetMaterial = mesh.material;
}
}
this.invading = false;
},
update() {
this.radiusSquared = this.data.radius * this.data.radius;
this.updateDebug();
},
updateDebug() {
const system = this.el.sceneEl.systems["personal-space-bubble"];
if (system.data.debug || this.data.debug) {
!this.el.object3DMap[DEBUG_OBJ] && this.el.setObject3D(DEBUG_OBJ, createSphereGizmo(this.data.radius));
} else if (this.el.object3DMap[DEBUG_OBJ]) {
this.el.removeObject3D(DEBUG_OBJ);
}
},
remove() {
this.el.sceneEl.systems["personal-space-bubble"].unregisterInvader(this);
},
setInvading(invading) {
if (this.targetMaterial) {
this.targetMaterial.opacity = invading ? this.data.invadingOpacity : 1;
this.targetMaterial.transparent = invading;
} else {
this.el.object3D.visible = !invading;
}
this.invading = invading;
}
});
/**
* Represents a personal space bubble on an entity.
* @namespace avatar/personal-space-bubble
* @component personal-space-bubble
*/
AFRAME.registerComponent("personal-space-bubble", {
schema: {
radius: { type: "number", default: 0.8 },
debug: { default: false }
},
init() {
this.system.registerBubble(this);
},
update() {
this.radiusSquared = this.data.radius * this.data.radius;
this.updateDebug();
},
updateDebug() {
if (this.system.data.debug || this.data.debug) {
!this.el.object3DMap[DEBUG_OBJ] && this.el.setObject3D(DEBUG_OBJ, createSphereGizmo(this.data.radius));
} else if (this.el.object3DMap[DEBUG_OBJ]) {
this.el.removeObject3D(DEBUG_OBJ);
}
},
remove() {
this.system.unregisterBubble(this);
}
});