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