Talking about Pocket Snap game, Game development, HTML5, Javascript and Phaser.
When playing with the first step of Pocket Snap prototype, you surely missed a target where to try to make balls land.
The original game features some “U” shaped targets, which remainds a bit “Trick Shot” tutorial series, which was built using Phaser 2 and Box2D, and will get updated soon with the new Plank.js library.
Meanwhile, let’s stick to Matter physics and see what I did with it:
Tap and hold to charge, release to fire the ball.
Try to make the ball land into the target.
The game restarts a couple of seconds after you fired the ball with some random parameters.
If you managed to hit the target, you will see a “Yeah” message.
At the bottom of the “U” shape there is a sensor. Sensors work just like normal bodies triggering collisions, but they are ethereal.
Look at the commented source code:
let game;
window.onload = function() {
let gameConfig = {
type: Phaser.AUTO,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: "thegame",
width: 600,
height: 600
},
scene: playGame,
physics: {
default: "matter",
matter: {
gravity: {
y: 1
},
debug: true
}
}
}
game = new Phaser.Game(gameConfig);
window.focus();
}
class playGame extends Phaser.Scene{
constructor(){
super("PlayGame");
}
create(){
// matter settings
this.matter.world.update30Hz();
this.matter.world.setBounds(10, 10, game.config.width - 20, game.config.height - 20);
// random cannon properties
let angle = Phaser.Math.Between(60, 75);
let width = Phaser.Math.Between(20, 30);
let length = Phaser.Math.Between(120, 250);
let tickness = Phaser.Math.Between(10, 12);
let position = new Phaser.Math.Vector2(game.config.width / 6, 550);
// bottom body
let bottomWidth = width + tickness * 2;
let bottomBody = this.matter.add.rectangle(position.x, position.y, bottomWidth, tickness, this.setProperties(true, angle));
// some trigonometry useful to find the origins of cannon side bodies
let bottomCathetus = (width + tickness) / 2;
let sideCathetus = (length + tickness) / 2;
let hypotenuse = Math.sqrt(Math.pow(bottomCathetus, 2) + Math.pow(sideCathetus, 2));
let bottomAngle = Phaser.Math.RadToDeg(Math.asin(sideCathetus / hypotenuse));
// side body 1
let firstSideOrigin = this.moveBy(position, hypotenuse, 90 - bottomAngle - angle)
this.matter.add.rectangle(firstSideOrigin.x, firstSideOrigin.y, tickness, length, this.setProperties(true, angle));
// side body 2
let secondSideOrigin = this.moveBy(position, hypotenuse, bottomAngle - 90 - angle)
this.matter.add.rectangle(secondSideOrigin.x, secondSideOrigin.y, tickness, length, this.setProperties(true, angle));
// trigger
let triggerOrigin = this.moveBy(position, (length - (width * 3 - tickness) / 2), -angle)
let trigger = this.matter.add.rectangle(triggerOrigin.x, triggerOrigin.y, width, width, this.setProperties(false, angle));
// cannon ball
let ballOrigin = this.moveBy(position, (width + length - (width * 3 - tickness) / 2), -angle)
this.matter.add.circle(ballOrigin.x, ballOrigin.y, width / 2, this.setProperties(false, angle, "ball"));
// constraint
let constraintLength = length + (tickness - width * 3) / 2;
this.constraint = this.matter.add.constraint(bottomBody, trigger, constraintLength, 1);
this.constraintFireLength = constraintLength + width;
this.constrainMinLength = width / 2 + tickness / 2;
// random target properties
let targetWidth = Phaser.Math.Between(80, 120);
let targetHeight = Phaser.Math.Between(120, 180);
let targetTickness = Phaser.Math.Between(10, 20);
let targetPosition = new Phaser.Math.Vector2(Phaser.Math.Between(game.config.width / 4 * 3, game.config.width / 5 * 4), game.config.height - Phaser.Math.Between(40, 80));
// target
this.matter.add.rectangle(targetPosition.x, targetPosition.y, targetWidth + targetTickness * 2, targetTickness, {
isStatic: true
});
this.matter.add.rectangle(targetPosition.x + (targetWidth + targetTickness) / 2, targetPosition.y - (targetHeight + targetTickness) / 2, targetTickness, targetHeight, {
isStatic: true
});
this.matter.add.rectangle(targetPosition.x - (targetWidth + targetTickness) / 2, targetPosition.y - (targetHeight + targetTickness) / 2, targetTickness, targetHeight, {
isStatic: true
});
// this is the sensor used
this.matter.add.rectangle(targetPosition.x, targetPosition.y - targetTickness, targetWidth, targetTickness, {
isStatic: true,
isSensor: true,
label: "goal"
});
// a text to show if the player hits the target
this.yeahText = this.add.text(game.config.width / 2, 200, "YEAH !!", {
fontFamily: "Arial",
fontSize: 64,
color: "#00ff00"
})
this.yeahText.setOrigin(0.5);
this.yeahText.setVisible(false);
// check for collision between the sensor and the ball
this.matter.world.on("collisionstart", function(event, bodyA, bodyB){
if((bodyA.label == "ball" && bodyB.label == "goal") || (bodyA.label == "goal" && bodyB.label == "ball")){
// show the text if the ball hits the sensor
this.yeahText.visible = true;
}
}, this);
// listeners and flags
this.input.on("pointerdown", this.charge, this);
this.input.on("pointerup", this.fire, this);
this.charging = false
}
// charge
charge(){
this.charging = true;
}
// fire: look how stiffness changes, then restart the game
fire(){
this.charging = false;
this.constraint.stiffness = 0.02
this.constraint.length = this.constraintFireLength;
this.time.addEvent({
delay: 3500,
callbackScope: this,
callback: function(){
this.scene.start("PlayGame");
},
});
}
// we reduce constraint length if charging
update(){
if(this.charging && this.constraint.length > this.constrainMinLength){
this.constraint.length -= 1;
}
}
// utility method to create an object with body properties
setProperties(isStatic, angle, label){
if(label == undefined){
label = "";
}
let radians = Phaser.Math.DegToRad(90 - angle);
return {
isStatic: isStatic,
angle: radians,
friction: 0,
label: label,
restitution: 0.4
}
}
// utility method to move a point by "distance" pixels in "degrees" direction
moveBy(point, distance, degrees){
let radians = Phaser.Math.DegToRad(degrees);
return new Phaser.Math.Vector2(point.x + distance * Math.cos(radians), point.y + distance * Math.sin(radians));
}
};
We managed to create targets and react when the player fires the ball into the hole, next time we’ll see how to move targets, meanwhile download the source code.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.