game had some very positive reviews and I decided it's time for a sequel.
The original was vanilla JS, this time we'll use the awesome Matter.js library to add a physics engine.
The Easter Chicken must knock down all the pigs: press the left/right button to change the angle. Hold and release the red oval to launch the chicken.
Lines [3-19] initiate the Matter.js engine. Read
[20-24] Setup the canvas and put in the top left corner. This makes it easier to calculate the coordinates of mouse clicks and screen touches.
[25-26] The image for the buttons.
[27-37] Declare variables.
[39-51] This function handles the release of the mouse button/screen touch. Launch the chicken if the ovalPressed variable is true.
[53-64] Handle mouse down/screen touch. If the left or right button is clicked, set the 'move' value accordingly. If the red oval is clicked and the chicken is not moving, start the shot.
[66-72] If the screen is touched, get the coordinates of the first touch and pass them to the 'down' function.
[74-83] Create a new chicken.
[85-118] (Re-)create the world. This is done when the game starts or restarts.
Planks are the brown ('wooden') rectangles that break on collision. Targets are the pigs. A floor is an unbreakable gray ('stone') rectangle. Floor2 is the larger stone for the chicken.
Planks, stones and pigs are placed randomly on the screen.
[120-123] Resize the canvas (if the mobile device is rotated or browser window is resized).
[126] Move the angle by the value determined by the buttons.
[127-132] Display the message.
[134-145] Check if a pig fell below the screen. If so, mark it as dead and reduce the count of targets remaining.
[146-149] If the chicken fell down, create a new one.
[150-153] Draw the line indicating the angle.
[155-157] If the oval is pressed, increase the strength of the shot.
[160-172] Check each collision. If a plank collides with the chicken, remove the plank.
[174-181] Mouse and touch event listeners.
[183-184] Do the initial resize and set the listener.
| <html> |
| <body> |
| <script src='matter.min.js' type='text/javascript'></script> |
| <script> |
| let Engine = Matter.Engine, |
| Render = Matter.Render, |
| World = Matter.World, |
| Bodies = Matter.Bodies, |
| Events = Matter.Events, |
| Body = Matter.Body; |
| let engine = Engine.create(); |
| let render = Render.create({ |
| element: document.body, |
| engine: engine |
| }); |
| let world = engine.world; |
| Engine.run(engine); |
| Render.run(render); |
| render.options.wireframes = false; |
| render.canvas.style.position = "absolute"; |
| render.canvas.style.left = "0"; |
| render.canvas.style.top = "0"; |
| let context = render.canvas.getContext('2d'); |
| context.font = 'bold 30px sans-serif'; |
| let buttons = new Image(); |
| buttons.src = 'buttons.png'; |
| let move = 0; |
| let angle = Math.PI / 4; |
| let chicken; |
| let targetCount; |
| let maxTargets = 5; |
| let strength = 0; |
| let ovalPressed = false; |
| let maxStrength = 200; |
| let planks = []; |
| let targets = []; |
| let counter = 0; |
| |
| function up(e) { |
| if (ovalPressed) |
| Body.applyForce(chicken, { |
| x: chicken.position.x, |
| y: chicken.position.y |
| }, { |
| x: Math.sin(angle) * strength / 1000, |
| y: -Math.cos(angle) * strength / 1000 |
| }); |
| ovalPressed = false; |
| move = 0; |
| strength = 0; |
| } |
| |
| function down(x, y) { |
| if (y > window.innerHeight * 0.83) { |
| |
| if (x < window.innerWidth * 0.25) |
| move = -0.02; |
| if (x > window.innerWidth * 0.75) |
| move = 0.02; |
| if (x < window.innerWidth * 0.75 && chicken.speed < 0.3 && x > window.innerWidth * 0.25) { |
| ovalPressed = true; |
| } |
| } |
| } |
| |
| function touchStart(event) { |
| var touchobj = event.changedTouches[0]; |
| let pointerX = touchobj.clientX; |
| let pointerY = touchobj.clientY; |
| down(pointerX, pointerY); |
| event.preventDefault(); |
| } |
| |
| function newchicken() { |
| chicken = Bodies.circle(100, 420, 20, { |
| render: { |
| sprite: { |
| texture: 'chicken.png' |
| } |
| } |
| }); |
| World.add(engine.world, chicken); |
| } |
| |
| function reset() { |
| planks.splice(0, planks.length); |
| targets.splice(0, targets.length); |
| targetCount = maxTargets; |
| let floor = Bodies.rectangle(100, 500, 50, 50, { |
| isStatic: true |
| }); |
| World.add(engine.world, floor); |
| |
| for (let i = 0; i < maxTargets; i++) { |
| let x = 100 + Math.random() * 700; |
| let y = 100 + Math.random() * 400; |
| let floor = Bodies.rectangle(x, y, 20, 20, { |
| isStatic: true |
| }); |
| floor.render.fillStyle = 'brown'; |
| let target = Bodies.circle(x, y - 25, 10, { |
| render: { |
| sprite: { |
| texture: 'pig.png' |
| } |
| } |
| }); |
| x = 150 + Math.random() * 400; |
| y = 100 + Math.random() * 400; |
| targets.push(target); |
| let floor2 = Bodies.rectangle(x, y, 20, 20, { |
| isStatic: true |
| }); |
| planks.push(floor); |
| World.add(engine.world, [floor, floor2, target]); |
| } |
| newchicken(); |
| } |
| |
| function resize() { |
| render.canvas.style.width = window.innerWidth; |
| render.canvas.style.height = window.innerHeight; |
| } |
| |
| Events.on(render, 'afterRender', function() { |
| angle = angle + move; |
| context.fillStyle = 'black'; |
| if (counter) { |
| context.fillText('You win! Play again!', 20, 50); |
| counter--; |
| } else |
| context.fillText(targetCount + ' remaining', 20, 50); |
| |
| for (let n = 0; n < maxTargets; n++) { |
| if (targets[n].position.y > 600 && targets[n].label != 'dead') { |
| targetCount--; |
| targets[n].label = 'dead'; |
| World.remove(engine.world, targets[n]); |
| if (targetCount == 0) { |
| counter = 500; |
| World.clear(world); |
| reset(); |
| } |
| } |
| } |
| if (chicken.position.y > 600) { |
| World.remove(world, chicken); |
| newchicken(); |
| } |
| context.beginPath(); |
| context.moveTo(chicken.position.x, chicken.position.y); |
| context.lineTo(chicken.position.x + (40 + strength) * Math.sin(angle), chicken.position.y - (40 + strength) * Math.cos(angle)); |
| context.stroke(); |
| |
| if (ovalPressed && strength < maxStrength) |
| strength = strength + 2; |
| context.drawImage(buttons, 0, 500); |
| }); |
| |
| Matter.Events.on(engine, 'collisionStart', function(event) { |
| var pairs = event.pairs.slice(); |
| let a = pairs[0].bodyA; |
| let b = pairs[0].bodyB; |
| if (a == chicken || b == chicken) { |
| for (let n = 0; n < planks.length; n++) { |
| let collisionCheck = planks[n]; |
| if (a == collisionCheck || b == collisionCheck) { |
| World.remove(world, [a]); |
| } |
| } |
| } |
| }); |
| |
| render.canvas.addEventListener('touchstart', touchStart); |
| render.canvas.addEventListener('touchend', up); |
| render.canvas.addEventListener('mouseup', up); |
| render.canvas.addEventListener('mousedown', function(e) { |
| let x = e.offsetX; |
| let y = e.offsetY; |
| down(x, y); |
| }); |
| |
| window.onresize = resize; |
| resize(); |
| reset(); |
| |
| </script> |
| </body> |
| </html> |