Justiciar
Justiciar

Reputation: 376

Collision detection with three.js

I have a pretty simple world I've created with three.js. I'm making a really basic plat-former and I'm trying to get collision detection to work when a user runs on top of a coin to update a text counter of how many coins have been touched. As of right now nothing occurs when i run over the coins, no JavaScript console errors either. Any help would be amazing.

I've been trying to use a raycaster to detect the collision. Here's some snippets of what I have, I can dump more or all 600ish lines of code if requested.'

<div id="info">
    <h1 style="color: white">0/10</h1>
</div>

Javascript:

var coin = [];
var raycaster;
var loader = new THREE.GLTFLoader();
... //Lots more variables I've left out for readability


initThree();
initCannon();
animate();

function initThree() {

            clock = new THREE.Clock();

            //Renderer
            var width = window.innerWidth;
            var height = window.innerHeight;

            renderer = new THREE.WebGLRenderer( {antialias: true} );
            renderer.setPixelRatio( window.devicePixelRatio );
            renderer.setSize( width, height );
            document.body.appendChild( renderer.domElement );

            scene = new THREE.Scene();
            scene.background = new THREE.Color( 0xb2b2b2 );
            scene.fog = new THREE.Fog( 0x9bacc6, 0, 7000 );

            //Camera
            camera = new THREE.PerspectiveCamera( 90, width/height, 1, 10000 );
            //camera.position.set( 3000, 3000, 3000 );
            camera.position.set( 0, 10, 0 );
            camera.lookAt( scene.position );

            //Controls      
            controls = new THREE.PointerLockControls( camera );

            var blocker = document.getElementById( 'blocker' );
            var instructions = document.getElementById( 'instructions' );

            //Lock control when focused
            instructions.addEventListener( 'click', function () {

                controls.lock();

            }, false );

            //Hide overlay
            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() );

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

            loadTrees();
            loadCoins();
            createLight();
            createGround();
            createWater();
            createSky();
        }

function loadCoins() {      

            var coinPath = '../models/gltf/coin/scene.gltf';
            loader.load( coinPath, function( gltf ) {
                coin[0] = gltf.scene;
                coin[0].position.set(3300,1500,1000);
                coin[0].scale.set(100,100,100);
                scene.add( coin[0] );
            });
            loader.load( coinPath, function( gltf ) {
                coin[1] = gltf.scene;
                coin[1].position.set(3300,1500,-1000);
                coin[1].scale.set(100,100,100);
                scene.add( coin[1] );
             ...
            });

    function animate() {
            requestAnimationFrame( animate );
            update();
            render();
        }

function update() {
            //controls.update();

            world.step( timeStep );

            ground.position.copy( groundBody.position );
            ground.quaternion.copy( groundBody.quaternion );

            var mixDelta = clock.getDelta();
            if( mixer != null) {
                mixer.update( mixDelta );
                mixer2.update( mixDelta );
                mixer3.update( mixDelta );
            }

            raycaster.ray.origin.copy( controls.getObject().position );
            raycaster.ray.origin.y -= 100;

            //checks to see if intersecting object array
            var intersections = raycaster.intersectObjects( coin );

            var touching = intersections.length > 0;

            var time = performance.now();
            var delta = ( time - prevTime ) / 1000;

            velocity.x -= velocity.x * 10 * delta;
            velocity.z -= velocity.z * 10 * delta;
            velocity.y -= 9.8 * 100 * delta; //100 = mass //9.8

            direction.z = Number( moveForward ) - Number( moveBackward );
            direction.x = Number( moveLeft ) - Number( moveRight );
            direction.normalize(); //consistent movement in all directions

            if( moveForward || moveBackward ) {
                velocity.z -= direction.z * 7500 * delta;
            }
            if( moveLeft || moveRight ) {
                velocity.x -= direction.x * 7500 * delta;
            }

            //Stops when runs into an object
            if ( touching === true ) {
                    coinCount += 1;
                    document.getElementById('info').innerHTML = coinCount + "/10";
            }


            controls.getObject().translateX( velocity.x * delta );
            controls.getObject().translateY( velocity.y * delta );
            controls.getObject().translateZ( velocity.z * delta );

            //Keep Player within bounds of game
            if ( controls.getObject().position.y < 200 ) {

                    velocity.y = 0;
                    controls.getObject().position.y = 200;

                    canJump = true;

            }               
            if ( controls.getObject().position.x < -4000 ) {
                velocity.x = 0;
                controls.getObject().position.x = -4000;
            }
            if ( controls.getObject().position.x > 4000 ) {
                velocity.x = 0;
                controls.getObject().position.x = 4000;
            }               
            if ( controls.getObject().position.z < -4000 ) {
                velocity.x = 0;
                controls.getObject().position.z = -4000;
            }
            if ( controls.getObject().position.z > 4000 ) {
                velocity.x = 0;
                controls.getObject().position.z = 4000;
            }

            prevTime = time;

            //Update Water
            water.material.uniforms[ "time" ].value += 1.0 / 60.0;

            //Spin Coins
            for( i=0; i<coin.length; i++ ) {
                coin[i].rotation.y += .05;
            }
        }

Upvotes: 0

Views: 992

Answers (1)

Mugen87
Mugen87

Reputation: 31076

Raycasting is not an ideal collision detection approach for this use case. Consider to represent your coins with simple bounding volumes like THREE.Box3 (an AABB) or THREE.Sphere (a bounding sphere). The player itself will be represented with such a bounding volume, too. In your animation loop you then test if the player's bounding volume intersects with certain objects in your game environment like your coins. This is a much more reliable test than working with a single ray since you can better represent 3D objects with a spatial extension.

BTW: If you are not yet familiar with bounding volumes, I highly recommend to study this topic a bit. AABBs and bounding spheres are very important entities in context of collision detection. A good resource for this is 3D Math Primer for Graphics and Game Development by Fletcher Dunn Ian Parberry, Chapter 9 Geometric Primitives.

Upvotes: 1

Related Questions