Adding colliders to stack of block

Hello, my name is Ivan and I’m new to phaser and game development.

I wanted to make a simple game where there’s a stack of blocks (color randomly generated) in the middle and 2 buttons corresponding to the blocks. If the button color clicked is the same with the block on the bottom of the stack, it’ll delete the block, otherwise, it’s a game over. The problem I’m encountering is that I can’t seem to make the block stack (or collide between each block). Here’s the look when I spawned the blocks.

And here’s the look when the blocks are finished moving.

And here’s my code for this game.

let sceneGame = new Phaser.Scene('Game');

sceneGame.preload = function()
{
this.load.path = ‘assets/’;
this.load.image(‘blockA’, ‘blockA.png’);
this.load.image(‘blockB’, ‘blockB.png’);
this.load.image(‘ground’, ‘floor.png’);
}
var buttonA;
var buttonB;
var totems;
var definer;
var grounds;
sceneGame.create = function()
{
buttonA = this.add.image(450, 580, ‘blockA’).setScale(2);
buttonB = this.add.image(50, 580, ‘blockB’).setScale(2);

buttonA.setInteractive();
buttonB.setInteractive();


totems = [];
definer = [];
grounds = [];

for(var i=0 ;i<7; i++){
    var totemRandomizer = Math.floor(Math.random()*2)+1;
    if(totemRandomizer == 1){
        var totem = this.physics.add.sprite(250,50+i*-100,'blockA').setScale(2);
    }
    else{
        var totem = this.physics.add.sprite(250,50+i*-100,'blockB').setScale(2);
    }
    definer.push(totemRandomizer);
    totem.body.gravity.y = 500;

    totems.push(totem);

    for(var j=i-1;j>=0;j--){
        this.physics.add.collider(totem,totems[j]);
    }
}

for(var i=0;i<5;i++){
    var ground = this.physics.add.sprite(50+i*100,700,'ground').setScale(2);
    ground.setImmovable(true);
    ground.body.allowGravity = false;
    grounds.push(ground);

    for(var j=0;j<totems.length;j++){
        this.physics.add.collider(ground,totems[j]);
    }
}

//red button
buttonA.on('pointerdown', function(){
    if(definer[0] == 1){
        totems[0].destroy();
        totems.shift();
        definer.shift();
        console.log(totems.length);
    }
    else{
        // console.log("game over");
        sceneGame.scene.start('Result');
    }
});

//green button
buttonB.on('pointerdown', function(){
    if(definer[0] == 2){
        totems[0].destroy();
        totems.shift();
        definer.shift();
        console.log(totems.length);
    }
    else{
        // console.log("game over");
        sceneGame.scene.start('Result');
    }
});

}

sceneGame.update = function(time, delta)
{
if(totems.length < 4){
var totemRandomizer = Math.floor(Math.random()*2)+1;
if(totemRandomizer == 1){
var totem = this.physics.add.sprite(250,-5850,‘blockA’).setScale(2);
}
else{
var totem = this.physics.add.sprite(250,-5850,‘blockB’).setScale(2);
}
definer.push(totemRandomizer);

    totem.body.gravity.y = 500;
    for(var j=i-1;j>=0;j--){
        this.physics.add.collider(totem,totems[j]);
    }
    this.physics.add.collider(grounds[2],totems[j]);
}

}

Thank you for your time reading and replying to my question.

Use only 2 colliders. Phaser will do all the combinations.

this.physics.add.collider(totems);
this.physics.add.collider(grounds, totems);

Arcade Physics tends to be unstable for stacking. You can try https://phaser.io/examples/v3/view/physics/arcade/custom-separate.

2 Likes

Thank you for the reply, where do I put the code this.physics.add.collider(totems); and this.physics.add.collider(grounds, totems); ? I tried putting them in the create function, but it seems that it didn’t change anything.

Anywhere in create() is fine.

If you see the stack collapse until only 2 blocks are atop each other, it’s because they are colliding but they’re eventually pushing through each other. That’s a limitation of Arcade Physics. You have to try something like physics/arcade/custom-separate to deal with it.

1 Like

I tried placing it in the create() function, here are the codes…

sceneGame.create = function()
{

//create buttons

buttonA = this.add.image(450, 580, 'blockA').setScale(2);

buttonB = this.add.image(50, 580, 'blockB').setScale(2);

buttonA.setInteractive();

buttonB.setInteractive();

totems = [];

definer = [];

grounds = [];

//pake body touching down sama ground, kalau engga collide sama totem sebelumnya?

//create totem

for(var i=0 ;i<7; i++){

    var totemRandomizer = Math.floor(Math.random()*2)+1;

    if(totemRandomizer == 1){

        var totem = this.physics.add.sprite(250,50+i*-100,'blockA').setScale(2);

    }

    else{

        var totem = this.physics.add.sprite(250,50+i*-100,'blockB').setScale(2);

    }

    definer.push(totemRandomizer);

    totem.body.gravity.y = 500;

    this.physics.add.collider(totems);

    this.physics.add.collider(grounds, totems);

    totems.push(totem);

}

//create ground

for(var i=0;i<5;i++){

    var ground = this.physics.add.sprite(50+i*100,700,'ground').setScale(2);

    ground.setImmovable(true);

    ground.body.allowGravity = false;

    grounds.push(ground);

}

//collider

this.physics.add.collider(totems);

this.physics.add.collider(grounds, totems);

//red button

buttonA.on('pointerdown', function(){

    if(definer[0] == 1){

        totems[0].destroy();

        totems.shift();

        definer.shift();

        console.log(totems.length);

    }

    else{

        sceneGame.scene.start('Result');

    }

});

//green button

buttonB.on('pointerdown', function(){

    if(definer[0] == 2){

        totems[0].destroy();

        totems.shift();

        definer.shift();

        console.log(totems.length);

    }

    else{

        sceneGame.scene.start('Result');

    }

});

}

But the result is like this,…

Is there any other way to fix this without using https://phaser.io/examples/v3/view/physics/arcade/custom-separate? Because I don’t understand them atm,…

Also, do you have discord or anything I can contact you with beside here?

Thank you.

What’s wrong with the result?

I’m not on Discord but there is a Phaser one, I’m sure they could help.

I spawned 7 blocks and it only stacks to 4 blocks,…

Okay, so there’s Discord group for Phaser?

You can try

let sceneGame = new Phaser.Scene({
  key: 'Game',
  physics: { arcade: { debug: true, fps: 300, overlapBias: 16 } }
});

It keeps the stack together but they wiggle around.

Another way is to not use Arcade Physics, instead something like https://rexrainbow.github.io/phaser3-rex-notes/docs/site/board-quadgrid/. But that’s more to learn.

Well, it worked,… but sometimes one block still overlapping another block,… and also I can’t spawn more than 15 blocks before the overlapping problem I mentioned above become common and the game start to lag. Is it because the FPS value is high? When I lowered the FPS to like 60 or 30, the overlapping problem becomes worse. So, it’s the limitation of the Arcade Physics ?

If you see more lag after that, then it’s probably the high fps.

I think you can add custom separation pretty easily. Where you’re creating each block, add

totem.body.customSeparateY = true;

And where you had the totems collider, remove and substitute

this.physics.add.collider(totems, totems, function(totem1, totem2) {
  var b1 = totem1.body;
  var b2 = totem2.body;

  if (b1.y > b2.y) {
    b2.y += b1.top - b2.bottom;
  } else {
    b1.y += b2.top - b1.bottom;
  }
});
1 Like

Sorry for the late reply, I’ve been busy with other stuff,… So, I did what you suggest and placed the
totem.body.customSeparateY = true;
in the loop for creating each block, and replaced
this.physics.add.collider(totems)
with the custom function. Here’s the code after I do the changes

sceneGame.create = function()
{

//create buttons

buttonA = this.add.image(450, 580, 'blockA').setScale(2);

buttonB = this.add.image(50, 580, 'blockB').setScale(2);

buttonA.setInteractive();

buttonB.setInteractive();

totems = [];

definer = [];

grounds = [];

//create totem

for(var i=0 ;i<13; i++){

    var totemRandomizer = Math.floor(Math.random()*2)+1;

    if(totemRandomizer == 1){

        var totem = this.physics.add.sprite(250,50+i*-100,'blockA').setScale(2);

    }

    else{

        var totem = this.physics.add.sprite(250,50+i*-100,'blockB').setScale(2);

    }

    definer.push(totemRandomizer);

    totem.body.gravity.y = 500;

    totem.body.customSeparateY = true;

    // this.physics.add.collider(totems);

    this.physics.add.collider(totems, totems, function(totem1, totem2){

        var b1 = totem1.body;

        var b2 = totem2.body;



        if(b1.y > b2.y){

            b2.y += b1.top - b2.bottom;

        }

        else{

            b1.y += b2.top - b1.bottom;

        }

    });

    this.physics.add.collider(grounds, totems);

    totems.push(totem);  

}

//create ground

for(var i=0;i<5;i++){

    var ground = this.physics.add.sprite(50+i*100,700,'ground').setScale(2);

    ground.setImmovable(true);

    ground.body.allowGravity = false;

    grounds.push(ground);

}


//red button

buttonA.on('pointerdown', function(){

    if(definer[0] == 1){

        totems[0].destroy();

        totems.shift();

        definer.shift();

    }

    else{

        sceneGame.scene.start('Result');

    }

});

//green button

buttonB.on('pointerdown', function(){

    if(definer[0] == 2){

        totems[0].destroy();

        totems.shift();

        definer.shift();

    }

    else{

        sceneGame.scene.start('Result');

    }

});

}

But the problem is now that the blocks didn’t collide with the grounds.

Also, antoher little question,… Why the sceneGame.create = function() { and } is not in the scrollable area?

So I’d forgotten — when you use customSeparateX or customSeparateY on a body then you need to do your own separation for all the collisions the body is in.

You can use the same function for the blocks vs. ground collider.

function separate(sprite1, sprite2) {
  var b1 = sprite1.body;
  var b2 = sprite2.body;

  if (b1.y > b2.y) {
    b2.y += b1.top - b2.bottom;
  } else {
    b1.y += b2.top - b1.bottom;
  }
}

sceneGame.create = function() {
  // …
  this.physics.add.collider(totems, grounds, separate);
  this.physics.add.collider(totems, totems, separate);
  // …
}
1 Like