UNPKG

cannon

Version:

A lightweight 3D physics engine written in JavaScript.

408 lines (310 loc) 15.9 kB
<!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>