Talking about Game development, HTML5, Javascript and Phaser.
I was wondering how to properly handle constraints in the Fling prototype when I probably came to a solution:
Basically a constraint is a rule which must be followed. In our case, the rule is “keep a certain distance between two bodies”. It seems quite an easy rule to follow, but what happens when the only way to keep the required distance causes two bodies to compenetrate?
Here we have a dilemma: keeping the distance and letting a body trespass another body or act as they were solid bodies – and actually they are – and break the constraint?
Look at this example: we have three static bodies at the top, three dynamic bodies at the bottom, three distance constraints with a little stiffness and a big static body in the middle of the screen.
What happens when we reduce the distance?
From left to right:
1 – the body trespasses the other body to satisfy the constraint. I simply contract the constraint and do not care about the rest. This is Matter.js default way of handling constraints.
2 – the constraint breaks. Each time I contract the constraint, I check constraint length with actual distance between the two bodies. If they differ, the constraint is removed.
3 – the constraint does not break and does not let the body trespass solid bodies. In this case too, each time I contract the constraint, I check constraint length with actual distance between the two bodies, but this time if they differ, constraint lenght is updated to match actual distance between the bodies.
Look at the source code:
let game;
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: 1
},
debug: true,
debugBodyColor: 0xff00ff,
debugWireframes: false
}
}
}
game = new Phaser.Game(gameConfig);
window.focus();
}
class playGame extends Phaser.Scene{
constructor(){
super("PlayGame");
}
create(){
this.matter.world.update30Hz();
this.matter.world.setBounds(10, 10, game.config.width - 20, game.config.height - 20);
let poly = this.matter.add.rectangle(game.config.width / 2, game.config.height / 2, game.config.width, 50, {
isStatic: true
});
this.topBodies = [];
this.bottomBodies = [];
this.constraints = [];
for(let i = 1; i <= 3; i++){
this.topBodies.push(this.matter.add.rectangle( game.config.width / 4 * i, 50, 20, 20, {
isStatic: true
}));
this.bottomBodies.push(this.matter.add.rectangle( game.config.width / 4 * i, 730, 20, 20));
let distance = Phaser.Math.Distance.Between(this.topBodies[i - 1].position.x, this.topBodies[i - 1].position.y, this.bottomBodies[i - 1].position.x, this.bottomBodies[i - 1].position.y);
this.constraints.push(this.matter.add.constraint(this.bottomBodies[i - 1], this.topBodies[i - 1], distance, 0.1));
}
}
update(){
this.constraints[0].length -= 2;
if(this.constraints[0].length < 0){
this.scene.start("PlayGame")
}
this.constraints[1].length -= 2;
if((Math.abs(Phaser.Math.Distance.Between(this.topBodies[1].position.x, this.topBodies[1].position.y, this.bottomBodies[1].position.x, this.bottomBodies[1].position.y) - this.constraints[1].length)) > 4){
this.matter.world.removeConstraint(this.constraints[1]);
}
this.constraints[2].length -= 2;
if((Math.abs(Phaser.Math.Distance.Between(this.topBodies[2].position.x, this.topBodies[2].position.y, this.bottomBodies[2].position.x, this.bottomBodies[2].position.y) - this.constraints[2].length)) > 4){
this.constraints[2].length = Phaser.Math.Distance.Between(this.topBodies[2].position.x, this.topBodies[2].position.y, this.bottomBodies[2].position.x, this.bottomBodies[2].position.y)
}
}
};
Which one should you use? It’s up to you, according to your needs. 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.