Reputation: 45
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
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