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.