Doggo
Doggo

Reputation: 11

I am having a problem with three.js collision detection

We are creating a three.js based game where players can eat food, currently we have a collision script but it is not working properly. Any help to get it working so our players can eat would be greatly appreciated.

The code is below.

Code snippet:

//sorry in advance for the crazy code structure o.o
			
//variables
var scene, renderer, rayCaster;
var WORLD, floor, FOOD, MWORLD;
var plr, camera, controls;
			
			function debugupdate()
			{
			window.plr = plr
			window.floor = floor
			window.WORLD = WORLD
			window.camera = camera
			window.controls = controls
			window.scene = scene
			window.FOOD = FOOD
			}
			setInterval(debugupdate, 1000)
			

			
//setup scene for gameplay
function InitGame()
{


scene = new THREE.Scene();

renderer = new THREE.WebGLRenderer();
rayCaster = new THREE.Raycaster();

renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.y = 8;
camera.position.z = 8;

controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.autoRotate = false;
controls.enablePan = false;
	
//controls.update() must be called after any manual changes to the camera's transform
//camera.position.set( 0, 20, 100 );
//controls.update();
//MWORLD = stuff mouse can detect
MWORLD = new THREE.Object3D();
MWORLD.name = 'MWORLD'
var floorgeo = new THREE.BoxGeometry(30, 0.5, 30);
var floormat = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
floor = new THREE.Mesh( floorgeo, floormat );
MWORLD.add(floor)
	WORLD = new THREE.Object3D();
    WORLD.add( MWORLD );
    WORLD.name = 'WORLD';
    scene.add(WORLD);
}

InitGame();

			//Mouse Stuff
			var MousePos;
			var PlrTarget;
				document.addEventListener('mousemove', MouseToWorld, false);
function MouseToWorld(event) {
	event.preventDefault();
	var mouse = {};
	mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
	mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
  var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
	vector.unproject( camera );
	var dir = vector.sub( camera.position ).normalize();
	var distance = - camera.position.z / dir.z;
	 MousePos = camera.position.clone().add( dir.multiplyScalar( distance ) );
	//console.log(MousePos)
  
    rayCaster.setFromCamera(mouse, camera);
    var intersects = rayCaster.intersectObjects(WORLD.getObjectByName('MWORLD').children, true);
    if (intersects.length > 0)
    //    console.log(intersects[0].point);
PlrTarget = intersects[0].point
	// Make the sphere follow the mouse
//	mouseMesh.position.set(event.clientX, event.clientY, 0);
};


			//Food Parent
			 FOOD = new THREE.Object3D();
			FOOD.name = 'FOOD'
			WORLD.add(FOOD)
			
			var fid = -1
			
			//Add Food Object should this be different?
	function AddFood()
	{
		fid = fid + 1
	var colors = ['red', 'blue', 'orange', 'yellow', 'pink', 'cyan'];

	var geometry = new THREE.SphereGeometry( 0.05 * 1.5, 32 / 4, 32 / 4 );
var material = new THREE.MeshBasicMaterial( {color: colors[Math.floor(Math.random() * colors.length)]} );
var sphere = new THREE.Mesh( geometry, material );
var geometry = new THREE.BoxGeometry( 0.1 * 1.5, 10, 0.1 * 1.5);//BoxGeometry for collision detection spheres were lagging like crazy :(
var material = new THREE.MeshBasicMaterial( {color: 'red'} );
		material.transparent = true
material.opacity = 0.2
var cube = new THREE.Mesh( geometry, material );
var foodrange = 15
cube.add(sphere);
cube.position.y = 0.25
cube.position.z = ((Math.random() * foodrange + 1) * (Math.round(Math.random()) * 2 - 1));
cube.position.x = ((Math.random() * foodrange + 1) * (Math.round(Math.random()) * 2 - 1));

cube.name = 'f' + fid
WORLD.getObjectByName('FOOD').add(cube)
	}

			//adds lots of food
    function InitFood()
	{
	var i
	for(i = 0; i < 150; i++)
	{
		AddFood();
	}
	}
			InitFood();

			
			
//Eats the food working I think...
function ConsumeFood(fid)
{
FOOD.remove(FOOD.getObjectByName(fid))
	plr.scale.x = plr.scale.x + 0.01
			plr.scale.y = plr.scale.y + 0.01
			plr.scale.z = plr.scale.z + 0.01
}

			
			//Creates Player
			function CreatePlr()
			{
			
			var geometry = new THREE.SphereGeometry( 0.5, 32, 32);//32 / 2
var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
var sphere = new THREE.Mesh( geometry, material );
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( {color: 'blue'} );
				material.transparent = true
material.opacity = 0.2
var cube = new THREE.Mesh( geometry, material );
				plr = new THREE.Object3D();
			plr.add(sphere);
			plr.add(cube);	
			scene.add(plr)
controls.target = plr.position

			}

CreatePlr();
setTimeout(Eat, 1500)

			
			//DETECT FOOD PLEASE HELP :(
			//sometimes works ok you have to have the food fairly deep within the player to detect
			//never eats as soon as you touch it
			//sometimes totally fails to detect piece of food until you go over it multiple times
			//sometimes random pieces of food are eaten even though they are not touched
			function Eat() {

var originPoint = plr.position.clone();

	
	for (var vertexIndex = 0; vertexIndex < plr.children[1].geometry.vertices.length; vertexIndex++)
	{		
		var localVertex = plr.children[1].geometry.vertices[vertexIndex].clone();
		var globalVertex = localVertex.applyMatrix4( plr.children[1].matrix );
		var directionVector = globalVertex.sub( plr.position );
		
		var ray = new THREE.Raycaster( originPoint, directionVector.clone().normalize() );
		var collisionResults = ray.intersectObjects( FOOD.children );
		if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() ) //if your touching the food or its in your player eat it
			
		collisionResults.forEach(function(food){
			console.log(food.object)//shows in console that food was detected and what piece of food it was
		ConsumeFood(food.object.name)//consume food based on name (f1, f2, f3)
		})
	
	}	

				
	setTimeout(Eat, 50)
			

} 
			
			var Time = new THREE.Clock();
			function PlrLerpSpeed(speed)
			{
					 var distance = plr.position.distanceTo(PlrTarget);
         var finalSpeed = (distance / speed);
         return Time.deltaTime / finalSpeed
				
			}
			
			var animate = function () {
				requestAnimationFrame( animate );
				if(PlrTarget){
				plr.lookAt(PlrTarget)
				//plr.position.lerp(PlrTarget, PlrLerpSpeed(1));
				plr.position.lerp(PlrTarget, 0.01 / (plr.position.distanceTo(PlrTarget) / 2));
				}
controls.update();
//plr.position = MousePos
/*var speed = 5; // units a second, the speed we want
var currentPoint = new THREE.Vector3();  // we will re-use it


// this part is in a function of event listener of, for example, a button
currentPoint.copy(plr.position); // cube is the object to move
var distance = currentPoint.distanceTo(MousePos)
var duration = (distance / speed) * 1000; // in milliseconds
new TWEEN.Tween(plr.position)
  .to(MousePos, duration) // destinationPoint is the object of destination
  .start();
	*/		

	renderer.render( scene, camera );
			};

			animate();
body { margin: 0; }
canvas { display: block; }
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Upvotes: 0

Views: 845

Answers (1)

Mugen87
Mugen87

Reputation: 31036

Since your player and food objects are basically spheres, you can implement a super fast and accurate collision detection with bounding spheres. Try it like so:

//variables
var scene, renderer, rayCaster;
var WORLD, floor, FOOD, MWORLD;
var plr, camera, controls;

function debugupdate() {
  window.plr = plr
  window.floor = floor
  window.WORLD = WORLD
  window.camera = camera
  window.controls = controls
  window.scene = scene
  window.FOOD = FOOD
}
setInterval(debugupdate, 1000)



//setup scene for gameplay
function InitGame() {


  scene = new THREE.Scene();

  renderer = new THREE.WebGLRenderer();
  rayCaster = new THREE.Raycaster();

  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.y = 8;
  camera.position.z = 8;

  controls = new THREE.OrbitControls(camera, renderer.domElement);
  controls.autoRotate = false;
  controls.enablePan = false;

  //controls.update() must be called after any manual changes to the camera's transform
  //camera.position.set( 0, 20, 100 );
  //controls.update();
  //MWORLD = stuff mouse can detect
  MWORLD = new THREE.Object3D();
  MWORLD.name = 'MWORLD'
  var floorgeo = new THREE.BoxGeometry(30, 0.5, 30);
  var floormat = new THREE.MeshBasicMaterial({
    color: 0x00ff00
  });
  floor = new THREE.Mesh(floorgeo, floormat);
  MWORLD.add(floor)
  WORLD = new THREE.Object3D();
  WORLD.add(MWORLD);
  WORLD.name = 'WORLD';
  scene.add(WORLD);
}

InitGame();

//Mouse Stuff
var MousePos;
var PlrTarget;
document.addEventListener('mousemove', MouseToWorld, false);

function MouseToWorld(event) {
  event.preventDefault();
  var mouse = {};
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
  vector.unproject(camera);
  var dir = vector.sub(camera.position).normalize();
  var distance = -camera.position.z / dir.z;
  MousePos = camera.position.clone().add(dir.multiplyScalar(distance));
  //console.log(MousePos)

  rayCaster.setFromCamera(mouse, camera);
  var intersects = rayCaster.intersectObjects(WORLD.getObjectByName('MWORLD').children, true);
  if (intersects.length > 0)
    //    console.log(intersects[0].point);
    PlrTarget = intersects[0].point
  // Make the sphere follow the mouse
  //	mouseMesh.position.set(event.clientX, event.clientY, 0);
};


//Food Parent
FOOD = new THREE.Object3D();
FOOD.name = 'FOOD'
WORLD.add(FOOD)

var fid = -1

//Add Food Object should this be different?
function AddFood() {
  fid = fid + 1
  var colors = ['red', 'blue', 'orange', 'yellow', 'pink', 'cyan'];

  var geometry = new THREE.SphereGeometry(0.05 * 1.5, 32 / 4, 32 / 4);
  var material = new THREE.MeshBasicMaterial({
    color: colors[Math.floor(Math.random() * colors.length)]
  });
  var sphere = new THREE.Mesh(geometry, material);

  var foodrange = 15
  sphere.position.y = 0.25
  sphere.position.z = ((Math.random() * foodrange + 1) * (Math.round(Math.random()) * 2 - 1));
  sphere.position.x = ((Math.random() * foodrange + 1) * (Math.round(Math.random()) * 2 - 1));

  sphere.name = 'f' + fid
  WORLD.getObjectByName('FOOD').add(sphere)
}

//adds lots of food
function InitFood() {
  var i
  for (i = 0; i < 150; i++) {
    AddFood();
  }
}
InitFood();



//Eats the food working I think...
function ConsumeFood(fid) {
  FOOD.remove(FOOD.getObjectByName(fid))
  plr.scale.x = plr.scale.x + 0.01
  plr.scale.y = plr.scale.y + 0.01
  plr.scale.z = plr.scale.z + 0.01
}


//Creates Player
function CreatePlr() {

  var geometry = new THREE.SphereGeometry(0.5, 32, 32); //32 / 2
  var material = new THREE.MeshBasicMaterial({
    color: 0xffff00
  });
  plr = new THREE.Mesh(geometry, material);
  scene.add(plr)
  controls.target = plr.position

}

CreatePlr();
setTimeout(Eat, 1500)

var spherePlayer = new THREE.Sphere();
var sphereFood = new THREE.Sphere();

//DETECT FOOD PLEASE HELP :(
//sometimes works ok you have to have the food fairly deep within the player to detect
//never eats as soon as you touch it
//sometimes totally fails to detect piece of food until you go over it multiple times
//sometimes random pieces of food are eaten even though they are not touched
function Eat() {

	spherePlayer.copy( plr.geometry.boundingSphere ).applyMatrix4( plr.matrixWorld );
	
	for ( var i = 0; i < FOOD.children.length; i ++ ) {
		
		var food = FOOD.children[ i ];
		
		sphereFood.copy( food.geometry.boundingSphere ).applyMatrix4( food.matrixWorld );
		
		if ( spherePlayer.intersectsSphere( sphereFood ) === true ) {
		
			ConsumeFood(food.name);
		
		}
		
	
	}

  setTimeout(Eat, 50)

}

var Time = new THREE.Clock();

function PlrLerpSpeed(speed) {
  var distance = plr.position.distanceTo(PlrTarget);
  var finalSpeed = (distance / speed);
  return Time.deltaTime / finalSpeed

}

var animate = function() {
  requestAnimationFrame(animate);
  if (PlrTarget) {
    plr.lookAt(PlrTarget)
    //plr.position.lerp(PlrTarget, PlrLerpSpeed(1));
    plr.position.lerp(PlrTarget, 0.01 / (plr.position.distanceTo(PlrTarget) / 2));
  }
  controls.update();
  //plr.position = MousePos
  /*var speed = 5; // units a second, the speed we want
  var currentPoint = new THREE.Vector3();  // we will re-use it


  // this part is in a function of event listener of, for example, a button
  currentPoint.copy(plr.position); // cube is the object to move
  var distance = currentPoint.distanceTo(MousePos)
  var duration = (distance / speed) * 1000; // in milliseconds
  new TWEEN.Tween(plr.position)
    .to(MousePos, duration) // destinationPoint is the object of destination
    .start();
  	*/

  renderer.render(scene, camera);
};

animate();
body { margin: 0; }
canvas { display: block; }
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>

Upvotes: 1

Related Questions