whothatisShake
whothatisShake

Reputation: 45

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/master/build/three.js"></script>
  <!-- <script src="lib/three.min.js"></script> -->
  <script src="https://github.com/mrdoob/three.js/blob/master/examples/js/WebGL.js"></script>
  <script src="https://github.com/mrdoob/three.js/blob/master/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/master/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>

Upvotes: 1

Views: 1076

Answers (1)

Ethan Hermsey
Ethan Hermsey

Reputation: 940

You are on the right path with the raycaster line.

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

As i read your code snippet, I am assuming the raycasting/jumping on the ground plane is already working. You want to know how to stop the player from moving into boxes in the world?

I would add another raycast in the direction the player is moving. To see if there is an object in the way. If you pass your players position and direction into the raycaster, it will shoot out a ray from the position towards the direction. Something in the line of this?

This is only to show the idea, you might want to implement it better, maybe even use boundingboxes, as in this example: https://stemkoski.github.io/Three.js/Collision-Detection.html

function animate() {
    requestAnimationFrame( animate );
    if ( controls.isLocked === true ) {
        raycaster.ray.origin.copy( controls.getObject().position );
        raycaster.ray.origin.y -= 10;
        raycaster.ray.origin.x -= 0;
        var intersections = raycaster.intersectObjects( objects );
        var onObject = intersections.length > 0;
        var time = performance.now();
        var 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;
        }

        ////EDIT


        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;

        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
        }
        ////EDIT


        if ( controls.getObject().position.y < 10 ) {
            velocity.y = 0;
            controls.getObject().position.y = 10;
            canJump = true;
        }
        prevTime = time;
    }
    renderer.render( scene, camera );
}

Upvotes: 2

Related Questions