UNPKG

p2s

Version:

A JavaScript 2D physics engine.

235 lines (191 loc) 7.23 kB
<!DOCTYPE html> <html lang="en"> <head> <title>TapBall - p2.js canvas game for mobile</title> <meta charset="utf-8"> <!-- No zooming, and device width viewport --> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <!-- include the physics library --> <script src="../../build/p2.js"></script> <style> /* Full screen canvas */ canvas, html, body { width: 100%; height: 100%; text-align: center; margin: 0; padding: 0; } /* Counter div at top */ #counter { width: 100%; position: fixed; top: 0; font-family: "Comic Sans MS"; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } </style> </head> <body> <div id="counter">Tap ball to start</div> <script> // Globals var canvas, ctx, w, h, zoom, world, ballBody, bottomPlaneBody; var ballRadius = 1, gameWidth = 6, gameHeight = 8; // Initialize and start rendering the game! init(); resetGame(); requestAnimationFrame(animate); // Initializes canvas, physics and input events function init(){ // Init canvas element and add it to the DOM canvas = document.createElement("CANVAS"); w = canvas.width = window.innerWidth * window.devicePixelRatio; h = canvas.height = window.innerHeight * window.devicePixelRatio; document.body.appendChild(canvas); ctx = canvas.getContext("2d"); ctx.lineWidth = 0.05; ctx.fillStyle = "white"; // Create a physics world world = new p2.World(); // Turn off friction and set some bounciness world.defaultContactMaterial.friction = 0; world.defaultContactMaterial.restitution = 0.5; // Create a physics body for the ball ballBody = new p2.Body({ mass: 1, position: [0, -2] }); ballBody.addShape(new p2.Circle({ // Give it a circle shape radius: ballRadius })); world.addBody(ballBody); // Add physics planes on the sides bottomPlaneBody = createPlane([0, -gameHeight/2], 0); createPlane([0, gameHeight/2 + ballRadius*2], Math.PI); // Top createPlane([-gameWidth/2, 0], -Math.PI / 2); // Left createPlane([gameWidth/2, 0], Math.PI / 2); // Right // Game over when the ball touches the bottom plane world.on('beginContact', function(evt){ if((evt.bodyA === ballBody && evt.bodyB === bottomPlaneBody) || evt.bodyA === bottomPlaneBody && evt.bodyB === ballBody){ resetGame(); } }); // 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 zoom = w < h ? w/gameWidth : h/gameHeight; ctx.scale(zoom, -zoom); // Zoom in and flip y axis // Add mouse event listeners canvas.addEventListener('mousedown', onKeyDown); canvas.addEventListener('touchstart', onKeyDown); } var inputType; function onKeyDown(event){ if(inputType && event.type !== inputType){ return; } inputType = event.type; // Convert the canvas coordinate to physics coordinates var position = getPhysicsCoord(event); // Check if the mouse clicked the ball body using hitTest var didHitBall = world.hitTest(position, [ballBody]).length !== 0; if(didHitBall){ var count = world.gravity[1] === 0 ? 0 : parseInt(counter.innerHTML) + 1; counter.innerHTML = count; // Apply an impulse on the ball var applyPoint = [0,0]; var dx = ballBody.position[0] - position[0]; var dy = 2; var len = Math.sqrt(dx*dx + dy*dy); var impulseSize = 15 + count / 3; var impulse = [ dx / len * impulseSize, dy / len * impulseSize ]; ballBody.applyImpulse(impulse, applyPoint); // Increase difficulty by increasing gravity! world.gravity[1] = - 10 - count; } } // Sets gravity to zero and the ball in initial position function resetGame(){ world.gravity[0] = world.gravity[1] = 0; ballBody.position[0] = 0; ballBody.position[1] = -2; ballBody.velocity[0] = ballBody.velocity[1] = 0; } // Convert a canvas coordiante to physics coordinate function getPhysicsCoord(mouseEvent){ var rect = canvas.getBoundingClientRect(); var clientX = mouseEvent.touches ? mouseEvent.touches[0].clientX : mouseEvent.clientX; var clientY = mouseEvent.touches ? mouseEvent.touches[0].clientY : mouseEvent.clientY; var x = (clientX - rect.left) * window.devicePixelRatio; var y = (clientY - rect.top) * window.devicePixelRatio; x = (x - w / 2) / zoom; y = -(y - h / 2) / zoom; return [x, y]; } // Creates a physics plane at a given position function createPlane(position, angle){ var planeBody = new p2.Body({ position: position, angle: angle }); planeBody.addShape(new p2.Plane()); world.addBody(planeBody); return planeBody; } // Animation loop var lastTime; var maxSubSteps = 5; // Max physics ticks per render frame var fixedDeltaTime = 1 / 30; // Physics "tick" delta time function animate(time){ requestAnimationFrame(animate); // Get the elapsed time since last frame, in seconds var deltaTime = lastTime ? (time - lastTime) / 1000 : 0; // Make sure the time delta is not too big (can happen if user switches browser tab) deltaTime = Math.min(1 / 10, deltaTime); // Move physics bodies forward in time world.step(fixedDeltaTime, deltaTime, maxSubSteps); lastTime = time; // Render scene render(); } function render(){ // Clear whole drawing area ctx.fillRect( -gameWidth, -gameHeight, gameWidth*2, gameHeight*2 ); // Draw the ball // We choose to render the interpolated position to get smooth animation. ctx.beginPath(); ctx.arc( ballBody.interpolatedPosition[0], ballBody.interpolatedPosition[1], ballRadius, 0, 2*Math.PI ); ctx.fill(); ctx.stroke(); // Draw the outer rectangle ctx.beginPath(); ctx.moveTo(-gameWidth/2,-gameHeight/2); ctx.lineTo(gameWidth/2,-gameHeight/2); ctx.lineTo(gameWidth/2,gameHeight/2); ctx.lineTo(-gameWidth/2,gameHeight/2); ctx.lineTo(-gameWidth/2,-gameHeight/2); ctx.stroke(); } </script> </body> </html>