How to add collision with raycast?

I want to add collision to my uni project, like in this example, however, to prevent passage through the object. Now in my project, collision work just on top. I used this. I want to create collision to object cube. How to add collision to sides? Which argument changes that? I tried change this

	raycaster.ray.origin.y -= 10;
	raycaster.ray.origin.x -= 0;

But as I noticed this changes the location of the upper collision. I have idea, that this one is what I need, but I don’t understand how it works…

raycaster = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, -1, 0 ), 0, 10 );

UPDATE

Using Ivan idea, now I can stop player moving into boxes. But when I come closer to that box, I get stuck and and can’t go anywhere. Even if the camera can’t see the box. So basically velocity x ir z axis become 0, and I can’t move. How to fix that?

UPDATED CODE:

/*
My WebGL App
*/
let mainContainer = null;
let fpsContainer
let stats = null;
let camera = null;
let renderer = null;
let scene = null;
let controls = null;
let raycaster = null;
let objects = [];
let moveForward = false;
let moveBackward = false;
let moveLeft = false;
let moveRight = false;
let canJump = false;

let prevTime = performance.now();
let velocity = new THREE.Vector3();
let direction = new THREE.Vector3();
// Global variables

function init() {
  if (THREE.WEBGL.isWebGLAvailable() === false) container.appendChild(WEBGL.getWebGLErrorMessage());
  fpsContainer = document.querySelector('#fps');
  mainContainer = document.querySelector('#webgl-secne');
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xEEEEEE); // http://www.colorpicker.com/
  scene.fog = new THREE.Fog(0xffffff, 0, 750);



  createStats();
  createCamera();
  createControls();
  createLights();
  createMeshes();
  createRenderer();
  renderer.setAnimationLoop(() => {
    update();
    render();
    animate();
  });
}

// Animations
function update() {


}

function animate() {

  requestAnimationFrame(animate);
  if (controls.isLocked === true) {
    //raycaster
    raycaster.ray.origin.copy(controls.getObject().position);
    const intersections = raycaster.intersectObjects(objects);
    const onObject = intersections.length > 0;
    //raycaster2
    //raycaster2.ray.origin.copy( controls.getObject().position );		
    //const intersections2 = raycaster2.intersectObjects( objects );
    //const saliaObject = intersections2.length > 0;
    //console.log(intersections2.length);

    const time = performance.now();
    const delta = (time - prevTime) / 1000;
    velocity.x -= velocity.x * 10.0 * delta;
    velocity.z -= velocity.z * 10.0 * delta;
    velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass
    direction.z = Number(moveForward) - Number(moveBackward);
    direction.x = Number(moveRight) - Number(moveLeft);
    direction.normalize(); // this ensures consistent movements in all directions
    if (moveForward || moveBackward) velocity.z -= direction.z * 400.0 * delta;
    if (moveLeft || moveRight) velocity.x -= direction.x * 400.0 * delta;
    if (onObject === true) {
      velocity.y = Math.max(0, velocity.y);
      canJump = true;
    }
    //if ( saliaObject === true ) {
    //velocity.z = Math.max( 0, velocity.z );

    //canJump = true;
    //}

    //UPDATE

    let collisionRange = 10; //if the mesh gets too close, the camera clips though the object...

    let tempVelocity = velocity.clone().multiplyScalar(delta) //get the delta velocity
    let nextPosition = controls.getObject().position.clone().add(tempVelocity);
    let tooClose = false;
    let playerPosition = controls.getObject().position;

    for (let i = 0; i < objects.length; i++) {
      let object = objects[i];
      let objectDirection = object.position.clone().sub(playerPosition).normalize();
      raycaster.set(nextPosition, objectDirection) //set the position and direction
      let directionIntersects = raycaster.intersectObject(object);
      if (directionIntersects.length > 0 && directionIntersects[0].distance < collisionRange) {
        //too close, stop player from moving in that direction...
        tooClose = true;
        break;
      }
    }




    if (tooClose == false) {
      controls.moveRight(-velocity.x * delta);
      controls.moveForward(-velocity.z * delta);
      controls.getObject().position.y += (velocity.y * delta); // new behavior
    }

    if (controls.getObject().position.y < 10) {
      velocity.y = 0;
      controls.getObject().position.y = 10;
      canJump = true;
    }

    prevTime = time;
  }
  renderer.render(scene, camera);
}


// Statically rendered content
function render() {
  stats.begin();

  renderer.render(scene, camera);
  stats.end();
}

// FPS counter
function createStats() {
  stats = new Stats();
  stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
  fpsContainer.appendChild(stats.dom);
}

// Camera object
function createCamera() {
  const fov = 75;
  const aspect = mainContainer.clientWidth / mainContainer.clientHeight;
  const near = 0.1;
  const far = 500; // meters
  camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.set(0, 10, 0);
}

// Interactive controls
function createControls() {
  controls = new THREE.PointerLockControls(camera, document.body);
  var blocker = document.getElementById('blocker');
  var instructions = document.getElementById('instructions');
  instructions.addEventListener('click', function() {
    controls.lock();
  }, false);
  controls.addEventListener('lock', function() {
    instructions.style.display = 'none';
    blocker.style.display = 'none';
  });
  controls.addEventListener('unlock', function() {
    blocker.style.display = 'block';
    instructions.style.display = '';
  });
  scene.add(controls.getObject());
  var onKeyDown = function(event) {
    switch (event.keyCode) {
      case 38: // up
      case 87: // w
        moveForward = true;
        break;
      case 37: // left
      case 65: // a
        moveLeft = true;
        break;
      case 40: // down
      case 83: // s
        moveBackward = true;
        break;
      case 39: // right
      case 68: // d
        moveRight = true;
        break;
      case 32: // space
        if (canJump === true) velocity.y += 350;
        canJump = false;
        break;
    }
  };
  var onKeyUp = function(event) {
    switch (event.keyCode) {
      case 38: // up
      case 87: // w
        moveForward = false;
        break;
      case 37: // left
      case 65: // a
        moveLeft = false;
        break;
      case 40: // down
      case 83: // s
        moveBackward = false;
        break;
      case 39: // right
      case 68: // d
        moveRight = false;
        break;
    }
  };
  document.addEventListener('keydown', onKeyDown, false);
  document.addEventListener('keyup', onKeyUp, false);

  raycaster = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3(0, -1, 0), 0, 5);
  //raycaster2 = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, 0 , -1 ),0,2);
  //raycaster2 = new THREE.Raycaster( new THREE.Vector2(), camera ); 	
}

// Light objects
function createLights() {

}

// Meshes and other visible objects
function createMeshes() {
  const geo = new THREE.PlaneBufferGeometry(1000, 1000);
  const mat = new THREE.MeshBasicMaterial({
    color: 0x98FB98
  });
  const plane = new THREE.Mesh(geo, mat);
  plane.rotateX(-Math.PI / 2);
  plane.receiveShadow = true;
  scene.add(plane);

  const geometry = new THREE.BoxBufferGeometry(20, 20, 20);
  const material = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    side: THREE.DoubleSide
  });
  const cube = new THREE.Mesh(geometry, material);
  cube.position.x = 15;
  cube.position.z = 10;
  cube.position.y = 15;
  cube.receiveShadow = true;
  cube.castShadow = true;
  scene.add(cube);
  objects.push(cube);

}

// Renderer object and features
function createRenderer() {
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(mainContainer.clientWidth, mainContainer.clientHeight);
  renderer.setPixelRatio(window.devicePixelRatio);
  //renderer.setClearColor(0xEEEEEE);
  mainContainer.appendChild(renderer.domElement);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

window.addEventListener('resize', onWindowResize, false);
init();
body {
  background-color: #fff;
  margin: 0px;
  overflow: hidden;
}

#info {
  color: #808080;
  font-family: Monospace;
  font-size: 1rem;
  text-align: center;
  position: absolute;
  top: 0px;
  width: 100%;
  padding: 5px;
  z-index: 1;
}

#webgl-secne {
  position: absolute;
  width: 100%;
  height: 100%;
}


/* Pointerlock */

#instructions {
  position: absolute;
  font-family: arial;
  width: 100%;
  height: 100%;
  display: -webkit-box;
  display: -moz-box;
  display: box;
  -webkit-box-orient: horizontal;
  -moz-box-orient: horizontal;
  box-orient: horizontal;
  -webkit-box-pack: center;
  -moz-box-pack: center;
  box-pack: center;
  -webkit-box-align: center;
  -moz-box-align: center;
  box-align: center;
  color: #ffffff;
  text-align: center;
  cursor: pointer;
  z-index: 1;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <title>Computer Graphics</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">

  <!-- Libraries -->
  <script src="https://github.com/mrdoob/three.js/blob/e9f31ad154d2cc314f37b5a8da4bdddd4f1bde7e/build/three.js"></script>
  <!-- <script src="lib/three.min.js"></script> -->
  <script src="https://github.com/mrdoob/three.js/blob/e9f31ad154d2cc314f37b5a8da4bdddd4f1bde7e/examples/js/WebGL.js"></script>
  <script src="https://github.com/mrdoob/three.js/blob/e9f31ad154d2cc314f37b5a8da4bdddd4f1bde7e/examples/js/libs/stats.min.js"></script>
  <!-- <script src="lib/controls/OrbitControls.js"></script> -->
  <!-- <script src="lib/controls/TrackballControls.js"></script>
  <script src="lib/controls/FlyControls.js"></script>
  <script src="lib/controls/FirstPersonControls.js"></script>-->
  <script src="https://github.com/mrdoob/three.js/blob/e9f31ad154d2cc314f37b5a8da4bdddd4f1bde7e/examples/js/controls/PointerLockControls.js"></script>
  <!-- <script src="lib/water/Reflector.js"></script>
  <script src="lib/water/Refractor.js"></script>
  <script src="lib/water/Water2.js"></script>
  <script src="lib/dat.gui.min.js"></script>
  <script src="lib/loaders/OBJLoader.js"></script>
  <script src="lib/loaders/MTLLoader.js"></script>
  <script src="lib/loaders/STLLoader.js"></script>
  <script src="lib/loaders/GLTFLoader.js"></script>
  <script src="lib/SceneUtils.js"></script>  -->

  <script src="app.js" defer></script>

  <link href="main.css" rel="stylesheet" type="text/css">
</head>

<body>
  <div id="info">Computer Graphics</div>
  <!-- Pointerlock -->
  <div id="blocker">
    <div id="instructions"><span style="font-size:40px">Click to play</span><br /> (W, A, S, D = Move, SPACE = Jump, MOUSE = Look around)
    </div>

  </div>
  <div id="webgl-secne">
    <!-- <canvas width="800" height="600"></canvas> -->
  </div>
  <div id="fps"></div>
</body>

</html>

I suggest you do it differently and avoid the usage of raycaster. It works fine in the demo but for your requirements it’s probably better to perform an AABB/AABB intersection test. Meaning you define an instance of Box3 for your player and test for collision with the AABBs of all other boxes via Box3.intersectsBox(). The process usually looks like so:

  • Save the current position of the player
  • Compute the new position of the current frame
  • Test for collision
  • If there is a collision, undo the position update
1 Like