'Phaser 3 (Game framework): collider callback is called, but somethimes object still passes through other object, instead of colliding

I'm working on a small flappy-bird-like-game demo. Everthing seems fine, but I have a small problem/question.

I setup a collider function, and the callback works as expected, when the "two" objects collide, but there is a strange behavior:

  • the white-square (the bird) can fly through the obstacles, when coming from the side
  • but cannot passthrough when coming from below or on above

BUT the callback is execute always.

blue arrow marks where the square passes through
green arrows mark where the square doesn't passthrough

screenshot of game error

I'm not sure if this is, because I'm using rectangles (they sometimes) cause problems, or because of my nested physics setup. I even tried to replaced the white rectangel with a sprite, but this still produces the same result/error.

For my demo: I could probablly just destroy the object and restart the level on collision, but I still would like to understand why this is happening? And how I can prevent this, inconsistant behavior.
I'm probably missing something, but couldn't find it, and I don't want to rebuild the application again.

So my question is: why is this happening? And How can I prevent this?

Here is the code:

const width = 400;
const height = 200;

const spacing = width / 4;

const levelSpeed = width / 4;
const flySpeed = width / 4;

var GameScene = {
    create (){

    let player = this.add.rectangle(width / 4, height / 2, 20, 20, 0xffffff);
    this.physics.add.existing(player);

    this.input.keyboard.on('keydown-SPACE', (event) => {
        if(player.body.velocity.y >= -flySpeed/2){
            player.body.setVelocityY(-flySpeed);
        }
    });

    player.body.onWorldBounds = true;
    player.body.setCollideWorldBounds(true );

    this.physics.world.on("worldbounds", function (body) {
      console.info('GAME OVER');
      player.y = height / 2;
      player.body.setVelocity(0);
    });

    this.pipes = [];

    for(let idx = 1; idx <= 10; idx++) {
        let obstacle = this.createObstacle(spacing * idx, Phaser.Math.Between(-height/3, 0));
        this.add.existing(obstacle);
        this.pipes.push(obstacle);
        
        this.physics.add.collider(obstacle.list[0], player)
        this.physics.add.collider(obstacle.list[1], player, _ => console.info(2))
    }
},

update(){
    this.pipes.forEach((item) => {
        if(item.x <= 0){
            item.body.x = spacing * 10;
        } 
    })
},
extend: {
    createObstacle (x, y){

        let topPipe = (new Phaser.GameObjects.Rectangle(this, 0, 0 , 20 , height / 2 ,0xff0000)).setOrigin(0);
        let bottomPipe = (new Phaser.GameObjects.Rectangle(this, 0, height/2 + 75, 20 , height / 2 ,0xff0000)).setOrigin(0);

        this.physics.add.existing(topPipe);
        this.physics.add.existing(bottomPipe);
        
        topPipe.body.setImmovable(true);
        topPipe.body.allowGravity = false;

        bottomPipe.body.setImmovable(true);
        bottomPipe.body.allowGravity = false;

        let obstacle = new Phaser.GameObjects.Container(this, x, y, [
            topPipe,
            bottomPipe
        ]);

        this.physics.add.existing(obstacle);

        obstacle.body.velocity.x = - levelSpeed;
        obstacle.body.allowGravity = false;

        return obstacle;
    }
}
};



var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width,
    height,
    scene: [GameScene],
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: flySpeed },
            debug: true
        },
    }
};

var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.js"></script>


Solution 1:[1]

Currently I just can assume, that the physics objects don't seem to work correct, when physics objects are nested.

Maybe I'm wrong, but since I rewrote the code again without nested physics - objects and it seems to work, I think my assumption Is correct. I shouldn't have tried to over engineer my code.

If someone has more insides, please let me know/share. I still not 100% sure, if this is the real reason, for the strange behavior.

Here the rewriten code:

const width = 400;
const height = 200;

const spacing = width / 4;

const levelSpeed = width / 4;
const flySpeed = width / 4;

var GameScene = {
    create (){

    let player = this.add.rectangle(width / 4, height / 2, 20, 20, 0xffffff);
    this.physics.add.existing(player);

    this.input.keyboard.on('keydown-SPACE', (event) => {
        if(player.body.velocity.y >= -flySpeed/2){
            player.body.setVelocityY(-flySpeed);
        }
    });

    player.body.onWorldBounds = true;
    player.body.setCollideWorldBounds(true );

    this.physics.world.on("worldbounds", function (body) {
      console.info('GAME OVER');
      player.x = width / 4;
      player.y = height / 2;
      player.body.setVelocity(0);
    });

    this.pipes = [];

    for(let idx = 1; idx <= 10; idx++) {
        let obstacle = this.createObstacle(spacing * idx, Phaser.Math.Between(-height/3, 0));
        this.add.existing(obstacle[0]);
        this.add.existing(obstacle[1]);
        this.pipes.push(...obstacle);
        
        this.physics.add.collider(obstacle[0], player)
        this.physics.add.collider(obstacle[1], player, _ => console.info(2))
    }
},

update(){
    this.pipes.forEach((item) => {
        if(item.x <= 0){
            item.body.x = spacing * 10;
        } 
        item.body.velocity.x = - levelSpeed;
    })
},
extend: {
    createObstacle (x, y){

        let topPipe = (new Phaser.GameObjects.Rectangle(this, x, -20 , 20 , height / 2 ,0xff0000)).setOrigin(0);
        let bottomPipe = (new Phaser.GameObjects.Rectangle(this, x, height/2 + 75, 20 , height / 2 ,0xff0000)).setOrigin(0);

        this.physics.add.existing(topPipe);
        this.physics.add.existing(bottomPipe);
        
        topPipe.body.setImmovable(true);
        topPipe.body.allowGravity = false;
        topPipe.body.velocity.x = - levelSpeed;

        bottomPipe.body.setImmovable(true);
        bottomPipe.body.allowGravity = false;
        bottomPipe.body.velocity.x = - levelSpeed;

        return [topPipe, bottomPipe];
    }
}
};



var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width,
    height,
    scene: [GameScene],
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: flySpeed },
            debug: true
        },
    }
};

var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.js"></script>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1