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> |