Newer
Older
Three.js / node_modules / cannon / examples / threejs_fps.html
@Tsubasa Tsubasa on 4 Aug 15 KB add
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>cannon.js + three.js physics shooter</title>
        <style>
            html, body {
                width: 100%;
                height: 100%;
            }

            body {
                background-color: #ffffff;
                margin: 0;
                overflow: hidden;
                font-family: arial;
            }

            #blocker {

                position: absolute;

                width: 100%;
                height: 100%;

                background-color: rgba(0,0,0,0.5);

            }

            #instructions {

                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;

            }

        </style>
    </head>
    <body>
        <script src="../libs/Three.js"></script>
        <script src="../build/cannon.js"></script>
        <script src="js/PointerLockControls.js"></script>

        <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, CLICK = Shoot)
            </div>

        </div>

        <script>

            var sphereShape, sphereBody, world, physicsMaterial, walls=[], balls=[], ballMeshes=[], boxes=[], boxMeshes=[];

            var camera, scene, renderer;
            var geometry, material, mesh;
            var controls,time = Date.now();

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

            var havePointerLock = 'pointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document;

            if ( havePointerLock ) {

                var element = document.body;

                var pointerlockchange = function ( event ) {

                    if ( document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element ) {

                        controls.enabled = true;

                        blocker.style.display = 'none';

                    } else {

                        controls.enabled = false;

                        blocker.style.display = '-webkit-box';
                        blocker.style.display = '-moz-box';
                        blocker.style.display = 'box';

                        instructions.style.display = '';

                    }

                }

                var pointerlockerror = function ( event ) {
                    instructions.style.display = '';
                }

                // Hook pointer lock state change events
                document.addEventListener( 'pointerlockchange', pointerlockchange, false );
                document.addEventListener( 'mozpointerlockchange', pointerlockchange, false );
                document.addEventListener( 'webkitpointerlockchange', pointerlockchange, false );

                document.addEventListener( 'pointerlockerror', pointerlockerror, false );
                document.addEventListener( 'mozpointerlockerror', pointerlockerror, false );
                document.addEventListener( 'webkitpointerlockerror', pointerlockerror, false );

                instructions.addEventListener( 'click', function ( event ) {
                    instructions.style.display = 'none';

                    // Ask the browser to lock the pointer
                    element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;

                    if ( /Firefox/i.test( navigator.userAgent ) ) {

                        var fullscreenchange = function ( event ) {

                            if ( document.fullscreenElement === element || document.mozFullscreenElement === element || document.mozFullScreenElement === element ) {

                                document.removeEventListener( 'fullscreenchange', fullscreenchange );
                                document.removeEventListener( 'mozfullscreenchange', fullscreenchange );

                                element.requestPointerLock();
                            }

                        }

                        document.addEventListener( 'fullscreenchange', fullscreenchange, false );
                        document.addEventListener( 'mozfullscreenchange', fullscreenchange, false );

                        element.requestFullscreen = element.requestFullscreen || element.mozRequestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen;

                        element.requestFullscreen();

                    } else {

                        element.requestPointerLock();

                    }

                }, false );

            } else {

                instructions.innerHTML = 'Your browser doesn\'t seem to support Pointer Lock API';

            }

            initCannon();
            init();
            animate();

            function initCannon(){
                // Setup our world
                world = new CANNON.World();
                world.quatNormalizeSkip = 0;
                world.quatNormalizeFast = false;

                var solver = new CANNON.GSSolver();

                world.defaultContactMaterial.contactEquationStiffness = 1e9;
                world.defaultContactMaterial.contactEquationRelaxation = 4;

                solver.iterations = 7;
                solver.tolerance = 0.1;
                var split = true;
                if(split)
                    world.solver = new CANNON.SplitSolver(solver);
                else
                    world.solver = solver;

                world.gravity.set(0,-20,0);
                world.broadphase = new CANNON.NaiveBroadphase();

                // Create a slippery material (friction coefficient = 0.0)
                physicsMaterial = new CANNON.Material("slipperyMaterial");
                var physicsContactMaterial = new CANNON.ContactMaterial(physicsMaterial,
                                                                        physicsMaterial,
                                                                        0.0, // friction coefficient
                                                                        0.3  // restitution
                                                                        );
                // We must add the contact materials to the world
                world.addContactMaterial(physicsContactMaterial);

                // Create a sphere
                var mass = 5, radius = 1.3;
                sphereShape = new CANNON.Sphere(radius);
                sphereBody = new CANNON.Body({ mass: mass });
                sphereBody.addShape(sphereShape);
                sphereBody.position.set(0,5,0);
                sphereBody.linearDamping = 0.9;
                world.add(sphereBody);

                // Create a plane
                var groundShape = new CANNON.Plane();
                var groundBody = new CANNON.Body({ mass: 0 });
                groundBody.addShape(groundShape);
                groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1,0,0),-Math.PI/2);
                world.add(groundBody);
            }

            function init() {

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

                scene = new THREE.Scene();
                scene.fog = new THREE.Fog( 0x000000, 0, 500 );

                var ambient = new THREE.AmbientLight( 0x111111 );
                scene.add( ambient );

                light = new THREE.SpotLight( 0xffffff );
                light.position.set( 10, 30, 20 );
                light.target.position.set( 0, 0, 0 );
                if(true){
                    light.castShadow = true;

                    light.shadowCameraNear = 20;
                    light.shadowCameraFar = 50;//camera.far;
                    light.shadowCameraFov = 40;

                    light.shadowMapBias = 0.1;
                    light.shadowMapDarkness = 0.7;
                    light.shadowMapWidth = 2*512;
                    light.shadowMapHeight = 2*512;

                    //light.shadowCameraVisible = true;
                }
                scene.add( light );



                controls = new PointerLockControls( camera , sphereBody );
                scene.add( controls.getObject() );

                // floor
                geometry = new THREE.PlaneGeometry( 300, 300, 50, 50 );
                geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );

                material = new THREE.MeshLambertMaterial( { color: 0xdddddd } );

                mesh = new THREE.Mesh( geometry, material );
                mesh.castShadow = true;
                mesh.receiveShadow = true;
                scene.add( mesh );

                renderer = new THREE.WebGLRenderer();
                renderer.shadowMapEnabled = true;
                renderer.shadowMapSoft = true;
                renderer.setSize( window.innerWidth, window.innerHeight );
                renderer.setClearColor( scene.fog.color, 1 );

                document.body.appendChild( renderer.domElement );

                window.addEventListener( 'resize', onWindowResize, false );

                // Add boxes
                var halfExtents = new CANNON.Vec3(1,1,1);
                var boxShape = new CANNON.Box(halfExtents);
                var boxGeometry = new THREE.BoxGeometry(halfExtents.x*2,halfExtents.y*2,halfExtents.z*2);
                for(var i=0; i<7; i++){
                    var x = (Math.random()-0.5)*20;
                    var y = 1 + (Math.random()-0.5)*1;
                    var z = (Math.random()-0.5)*20;
                    var boxBody = new CANNON.Body({ mass: 5 });
                    boxBody.addShape(boxShape);
                    var boxMesh = new THREE.Mesh( boxGeometry, material );
                    world.add(boxBody);
                    scene.add(boxMesh);
                    boxBody.position.set(x,y,z);
                    boxMesh.position.set(x,y,z);
                    boxMesh.castShadow = true;
                    boxMesh.receiveShadow = true;
                    boxes.push(boxBody);
                    boxMeshes.push(boxMesh);
                }


                // Add linked boxes
                var size = 0.5;
                var he = new CANNON.Vec3(size,size,size*0.1);
                var boxShape = new CANNON.Box(he);
                var mass = 0;
                var space = 0.1 * size;
                var N = 5, last;
                var boxGeometry = new THREE.BoxGeometry(he.x*2,he.y*2,he.z*2);
                for(var i=0; i<N; i++){
                    var boxbody = new CANNON.Body({ mass: mass });
                    boxbody.addShape(boxShape);
                    var boxMesh = new THREE.Mesh(boxGeometry, material);
                    boxbody.position.set(5,(N-i)*(size*2+2*space) + size*2+space,0);
                    boxbody.linearDamping = 0.01;
                    boxbody.angularDamping = 0.01;
                    // boxMesh.castShadow = true;
                    boxMesh.receiveShadow = true;
                    world.add(boxbody);
                    scene.add(boxMesh);
                    boxes.push(boxbody);
                    boxMeshes.push(boxMesh);

                    if(i!=0){
                        // Connect this body to the last one
                        var c1 = new CANNON.PointToPointConstraint(boxbody,new CANNON.Vec3(-size,size+space,0),last,new CANNON.Vec3(-size,-size-space,0));
                        var c2 = new CANNON.PointToPointConstraint(boxbody,new CANNON.Vec3(size,size+space,0),last,new CANNON.Vec3(size,-size-space,0));
                        world.addConstraint(c1);
                        world.addConstraint(c2);
                    } else {
                        mass=0.3;
                    }
                    last = boxbody;
                }
            }

            function onWindowResize() {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize( window.innerWidth, window.innerHeight );
            }

            var dt = 1/60;
            function animate() {
                requestAnimationFrame( animate );
                if(controls.enabled){
                    world.step(dt);

                    // Update ball positions
                    for(var i=0; i<balls.length; i++){
                        ballMeshes[i].position.copy(balls[i].position);
                        ballMeshes[i].quaternion.copy(balls[i].quaternion);
                    }

                    // Update box positions
                    for(var i=0; i<boxes.length; i++){
                        boxMeshes[i].position.copy(boxes[i].position);
                        boxMeshes[i].quaternion.copy(boxes[i].quaternion);
                    }
                }

                controls.update( Date.now() - time );
                renderer.render( scene, camera );
                time = Date.now();

            }

            var ballShape = new CANNON.Sphere(0.2);
            var ballGeometry = new THREE.SphereGeometry(ballShape.radius, 32, 32);
            var shootDirection = new THREE.Vector3();
            var shootVelo = 15;
            var projector = new THREE.Projector();
            function getShootDir(targetVec){
                var vector = targetVec;
                targetVec.set(0,0,1);
                projector.unprojectVector(vector, camera);
                var ray = new THREE.Ray(sphereBody.position, vector.sub(sphereBody.position).normalize() );
                targetVec.copy(ray.direction);
            }

            window.addEventListener("click",function(e){
                if(controls.enabled==true){
                    var x = sphereBody.position.x;
                    var y = sphereBody.position.y;
                    var z = sphereBody.position.z;
                    var ballBody = new CANNON.Body({ mass: 1 });
                    ballBody.addShape(ballShape);
                    var ballMesh = new THREE.Mesh( ballGeometry, material );
                    world.add(ballBody);
                    scene.add(ballMesh);
                    ballMesh.castShadow = true;
                    ballMesh.receiveShadow = true;
                    balls.push(ballBody);
                    ballMeshes.push(ballMesh);
                    getShootDir(shootDirection);
                    ballBody.velocity.set(  shootDirection.x * shootVelo,
                                            shootDirection.y * shootVelo,
                                            shootDirection.z * shootVelo);

                    // Move the ball outside the player sphere
                    x += shootDirection.x * (sphereShape.radius*1.02 + ballShape.radius);
                    y += shootDirection.y * (sphereShape.radius*1.02 + ballShape.radius);
                    z += shootDirection.z * (sphereShape.radius*1.02 + ballShape.radius);
                    ballBody.position.set(x,y,z);
                    ballMesh.position.set(x,y,z);
                }
            });

        </script>
    </body>
</html>