r/threejs • u/Familiar-Object9912 • 52m ago
Help CANNON.Ray (from cannon-es.js) unexplained bug inside my Three.js game, creating a roadblock in my game development.
https://reddit.com/link/1v3ud9w/video/q9i39whjlueh1/player
(Repost of my Schrodinger's Post (1.6K views but only 2 people commenting who haven't responded yet), this time only focusing on one issue)
So I was trying to get an interaction system going in a first person game I'm making. I don't know how to word this properly, but the ray intersection test only seems to work when pointing a ray from +x +y +z to -x -y -z, or from -x +y +z to +x -y -z, or all 8 combinations like that.
Notes:
- The video showcases faulty ray detection of the floor
- In the video, the 2 cubes, the red one and the green one, represent the starting and ending points of the raycaster respectively. Please note that the character body collision is excluded by not being in the intWorld.
- I tried messing around with the second parameter of intersectWorld() - with no success
- I have confirmed that the direction is in fact getting updated by intersectWorld()
- I have digged through the source code, and tried different fixes and workarounds - also with no success
- Nope, Three.js's raycasts won't work because this is a first person game with pointer lock, remember!
- For all of you who want me to switch to Rapier, I'd have to re-write the whole entire character movement script, which I have yoinked the tricky bits from PointerLockControlsCannon, except Rapier's way of handling events is kind of weird and you can't just slap down an event listener and call it a day
- CANNON.Ray's source code
Here is a very simplified snippet of my code:
import * as THREE from "three"; // three.js
import * as CANNON from "cannon"; // cannon-es.js
/* -----------------------------
Insert class declaration here
----------------------------- */
// this gets called each animation frame
updatePhysics() {
// this.world - main collision physics world
this.world.step(1 / 60);
// this.intWorld - contains the same colliders (for now) as this.world except the character collider
this.intWorld.step(1 / 60);
// Math abominations that are too weird for me to explain and I don't want to touch them,
// because they do in fact work; I trial&errored them.
this.camera.position.copy(this.characterBody.position);
this.camera.position.add(new THREE.Vector3(0, 0.75, 0));
let relCameraDir = new THREE.Vector3(0, 0, -1);
relCameraDir.applyAxisAngle(new THREE.Vector3(-1, 0, 0), this.cameraYY);
relCameraDir.applyAxisAngle(new THREE.Vector3(0, -1, 0), this.cameraAA);
let absCameraDir = new THREE.Vector3(0, 0, 0);
absCameraDir.copy(relCameraDir);
absCameraDir.multiplyScalar(50);
absCameraDir.add(this.camera.position);
let fromCameraDir = new THREE.Vector3(0, 0, 0);
fromCameraDir.copy(relCameraDir);
fromCameraDir.multiplyScalar(0);
fromCameraDir.add(this.camera.position);
// These are the 2 cube meshes that confirm that the positions the abomination above calculated are indeed correct.
this.rayDebug.position.copy(fromCameraDir);
this.rayDebug2.position.copy(absCameraDir);
// this.interactionRaycast - CANNON.Ray that's presumably bugging out.
this.interactionRaycast.from.copy(fromCameraDir);
this.interactionRaycast.to.copy(absCameraDir);
console.log(this.interactionRaycast.from, this.interactionRaycast.to, this.interactionRaycast.direction);
// This casts the ray and displays the HTML overlay
if (this.interactionRaycast.intersectWorld(this.intWorld, {from: fromCameraDir, to: absCameraDir})) {
debug.innerHTML = '<span style="color: green">Ray has collided!</span>';
} else {
debug.innerHTML = '<span style="color: red">No collisions detected</span>';
}
}
// This places one cube of the floor you see (visuals ommited).
placeCube(position) {
const body = new CANNON.Body({
mass: 0,
shape: WorldManager.BIG_CUBE_SHAPE,
material: (new CANNON.Material({friction: 0, restitution: 0})),
});
body.position.copy(position);
this.world.addBody(body);
this.intWorld.addBody(body);
}
// ...