UNPKG

p2s

Version:

A JavaScript 2D physics engine.

274 lines (231 loc) 8.31 kB
<!DOCTYPE html> <html lang="en"> <head> <title>p2.js Platformer example</title> <meta charset="utf-8"> <script src="../../build/p2.js"></script> </head> <body> <!-- The canvas, where we draw stuff --> <canvas width="600" height="400" id="myCanvas"></canvas> <p>Use arrow keys to control character</p> <script> var canvas, ctx, w, h, zoom=50, jumpSpeed=6, walkSpeed=2, timeStep=1/60, maxSubSteps=10, world, characterBody, planeBody, platforms=[], boxes=[]; var buttons = { space: 0, left: 0, right: 0 }; init(); requestAnimationFrame(animate); function init(){ // Init canvas canvas = document.getElementById("myCanvas"); w = canvas.width; h = canvas.height; ctx = canvas.getContext("2d"); ctx.lineWidth = 1/zoom; // Init world world = new p2.World(); world.defaultContactMaterial.friction = 0.5; world.setGlobalStiffness(1e5); // Init materials var groundMaterial = new p2.Material(), characterMaterial = new p2.Material(), boxMaterial = new p2.Material(); // Add a character body characterShape = new p2.Box({ width: 0.5, height: 1, material: characterMaterial }); characterBody = new p2.Body({ mass: 1, position:[0,3], fixedRotation: true, damping: 0.5 }); characterBody.addShape(characterShape); world.addBody(characterBody); // Add a ground plane planeShape = new p2.Plane({ material: groundMaterial }); planeBody = new p2.Body({ position:[0,-1] }); planeBody.addShape(planeShape); world.addBody(planeBody); // Add platforms var platformPositions = [[2,0],[0,1],[-2,2]]; for(var i=0; i<platformPositions.length; i++){ var platformBody = new p2.Body({ mass: 0, // Static position:platformPositions[i], type: p2.Body.KINEMATIC }); var platformShape = new p2.Box({ width: 1, height: 0.3, material: groundMaterial }); platformBody.addShape(platformShape); world.addBody(platformBody); platforms.push(platformBody); } // Add movable boxes var boxPositions = [[2,1],[0,2],[-2,3]]; for(var i=0; i<boxPositions.length; i++){ var boxBody = new p2.Body({ mass: 1, position:boxPositions[i] }); var boxShape = new p2.Box({ width: 0.8, height: 0.8, material: boxMaterial }); boxBody.addShape(boxShape); world.addBody(boxBody); boxes.push(boxBody); } // Init contactmaterials var groundCharacterCM = new p2.ContactMaterial(groundMaterial, characterMaterial,{ friction : 0, // No friction between character and ground }); var boxCharacterCM = new p2.ContactMaterial(boxMaterial, characterMaterial,{ friction : 0, // No friction between character and boxes }); var boxGroundCM = new p2.ContactMaterial(boxMaterial, groundMaterial,{ friction : 0.6, // Between boxes and ground }); world.addContactMaterial(groundCharacterCM); world.addContactMaterial(boxCharacterCM); world.addContactMaterial(boxGroundCM); // Allow pass through platforms from below var passThroughBody; world.on('beginContact', function (evt){ if(evt.bodyA !== characterBody && evt.bodyB !== characterBody) return; var otherBody = evt.bodyA === characterBody ? evt.bodyB : evt.bodyA; if(platforms.indexOf(otherBody) !== -1 && otherBody.position[1] > characterBody.position[1]){ passThroughBody = otherBody; } }); // Disable any equations between the current passthrough body and the character world.on('preSolve', function (evt){ for(var i=0; i<evt.contactEquations.length; i++){ var eq = evt.contactEquations[i]; if((eq.bodyA === characterBody && eq.bodyB === passThroughBody) || eq.bodyB === characterBody && eq.bodyA === passThroughBody){ eq.enabled = false; } } for(var i=0; i<evt.frictionEquations.length; i++){ var eq = evt.frictionEquations[i]; if((eq.bodyA === characterBody && eq.bodyB === passThroughBody) || eq.bodyB === characterBody && eq.bodyA === passThroughBody){ eq.enabled = false; } } }); world.on('endContact', function (evt){ if((evt.bodyA === characterBody && evt.bodyB === passThroughBody) || evt.bodyB === characterBody && evt.bodyA === passThroughBody){ passThroughBody = undefined; } }); } function drawBox(body){ ctx.beginPath(); var x = body.interpolatedPosition[0], y = body.interpolatedPosition[1], s = body.shapes[0]; ctx.save(); ctx.translate(x, y); // Translate to the center of the box ctx.rotate(body.interpolatedAngle); // Rotate to the box body frame ctx.fillRect(-s.width/2, -s.height/2, s.width, s.height); ctx.restore(); } function drawPlane(){ var y1 = planeBody.interpolatedPosition[1], y0 = -h/zoom/2, x0 = -w/zoom/2, x1 = w/zoom/2; ctx.fillRect(x0, y0, x1-x0, y1-y0); } function render(){ // Clear the canvas ctx.clearRect(0,0,w,h); // Transform the canvas // Note that we need to flip the y axis since Canvas pixel coordinates // goes from top to bottom, while physics does the opposite. ctx.save(); ctx.translate(w/2, h/2); // Translate to the center ctx.scale(zoom, -zoom); // Zoom in and flip y axis // Draw all bodies ctx.strokeStyle='none'; ctx.fillStyle='green'; drawPlane(); for(var i=0; i<platforms.length; i++){ drawBox(platforms[i]); } ctx.fillStyle='red'; drawBox(characterBody); for(var i=0; i<boxes.length; i++){ drawBox(boxes[i]); } // Restore transform ctx.restore(); } world.on('postStep', function(){ for(var i=0; i<platforms.length; i++){ platforms[i].velocity[0] = 2*Math.sin(world.time); } // Apply button response characterBody.velocity[0] = walkSpeed * (buttons.right - buttons.left); }); var lastTime; // Animation loop function animate(time){ requestAnimationFrame(animate); var dt = lastTime ? (time - lastTime) / 1000 : 0; dt = Math.min(1 / 10, dt); // Move physics bodies forward in time world.step(timeStep, dt, maxSubSteps); // Render scene render(); lastTime = time; } function checkIfCanJump(){ for(var i=0; i<world.narrowphase.contactEquations.length; i++){ var c = world.narrowphase.contactEquations[i]; if(c.bodyA === characterBody || c.bodyB === characterBody){ var d = c.normalA[1]; if(c.bodyA === characterBody) d *= -1; if(d > 0.5) return true; } } return false; } window.onkeydown = function(event){ switch(event.keyCode){ case 38: // up case 32: // space if(!buttons.space){ if(checkIfCanJump()) characterBody.velocity[1] = jumpSpeed; buttons.space = true; } break; case 39: buttons.right = 1; break; case 37: buttons.left = 1; break; } } window.onkeyup = function(event){ switch(event.keyCode){ case 38: // up case 32: buttons.space = 0; break; case 39: buttons.right = 0; break; case 37: buttons.left = 0; break; } } </script> </body> </html>