Talking about Mikey Hooks game, Game development, HTML5, Javascript and Phaser.
Mikey Hooks is a great game. Fullstop.
I liked so much the way the player used the hook, and I built prototypes using Flash + Box2D, Flash + Nape (Nape seems to be discontinued), Phaser + Box2D and Unity.
No matter the language, engine or framework you are using to build your hooks, they will all rely on a constraint called distance joint.
A constraint on a system is a parameter that the system must obey. In this case, the constraint is a distance joint which says “no matter what happens, there must be a given distance between two bodies”.
And that’s all:
Click on a box and hold to create a distance joint from the ball to the box working like a hook and start swinging by clicking and holding on different boxes.
The code is just a matter of checking if we clicked on a box, then create the joint until the mouse is released or a collision occurred.
Here it is, fully commented:
let game;
let gameOptions = {
gravity: 1, // game gravity
terrainObjects: 20, // amount of terrain objects
ballRadius: 20, // radius of the ball
constraintSpeed: 4, // constraint shrinkage speed
minBoxSize: 50, // minimum box size
maxBoxSize: 200 // maximum box size
}
const WALL = 0;
const BALL = 1;
window.onload = function() {
// game configuration
let gameConfig = {
type: Phaser.AUTO,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: "thegame",
width: 1334,
height: 750
},
scene: playGame,
physics: {
default: "matter",
matter: {
gravity: {
y: gameOptions.gravity
},
debug: true
}
}
}
game = new Phaser.Game(gameConfig);
window.focus();
}
class playGame extends Phaser.Scene{
constructor(){
super("PlayGame");
}
create(){
// I want physics world to be updated 30 times per second
this.matter.world.update30Hz();
// adding world bounds. Basically four walls
this.matter.world.setBounds(10, 10, game.config.width - 20, game.config.height - 20);
// placing some random static boxes labeled as WALL
for (let i = 0; i < gameOptions.terrainObjects; i++){
let posX = Phaser.Math.Between(0, game.config.width);
let posY = Phaser.Math.Between(0, game.config.height);
let width = Phaser.Math.Between(gameOptions.minBoxSize, gameOptions.maxBoxSize);
let height = Phaser.Math.Between(gameOptions.minBoxSize, gameOptions.maxBoxSize);
let poly = this.matter.add.rectangle(posX, posY, width, height, {
isStatic: true
});
poly.label = WALL;
}
// adding a bouncing ball labeled as BALL
this.ball = this.matter.add.circle(game.config.width / 2, game.config.height / 2, gameOptions.ballRadius, {
restitution: 0.5
});
this.ball.label = BALL;
// event listeners
this.input.on("pointerdown", this.fireHook, this);
this.input.on("pointerup", this.releaseHook, this);
// no ropes at the beginning
this.rope = null;
// when the ball collides on something, we'll remove the hook
this.matter.world.on("collisionstart", function(e, b1, b2){
if(b2.label == BALL){
this.releaseHook();
}
}, this)
}
// method to fire the hook
fireHook(e){
// getting all bodies
let bodies = this.matter.world.localWorld.bodies;
// looping through bodies
for(let i = 0; i < bodies.length; i++){
// getting body vertices
let vertices = bodies[i].parts[0].vertices;
// do the vertices contain the pointer AND the body is labeled as WALL?
if(Phaser.Physics.Matter.Matter.Vertices.contains(vertices, e.position) && bodies[i].label == WALL){
// calculate the distance between the ball and the body
let distance = Phaser.Math.Distance.Between(this.ball.position.x, this.ball.position.y, bodies[i].position.x, bodies[i].position.y)
// is the distance greater than ball radius?
if(distance > gameOptions.ballRadius){
// add the constraint
this.rope = this.matter.add.constraint(this.ball, bodies[i], distance, 0);
}
break;
}
}
}
// method to remove the hook
releaseHook(){
// is there a constraint? Remove it
if(this.rope){
this.matter.world.removeConstraint(this.rope);
this.rope = null;
}
}
// method to be executed at every frame
update(){
// is there a constraint? Shrink it
if(this.rope){
this.rope.length -= gameOptions.constraintSpeed;
}
}
};
At the moment the hook always starts from the center of the box, but I am working on a more interesting way using raycasting… stay tuned and meanwhile download the source code of this example.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.