Arcade Physics body flickering

Hey community!

I ran into a problem when coding the logic “a player plants the bomb”, or from the Arcade Physics perspective — a body needs to be spawned on keypress at the position of another body.

When I press “space”, you can see that a newly created body flickers for a moment, before going into its final position. Is it a bug? If not, is there a workaround to this?

Here is the code to reproduce the issue (you can test it here https://labs.phaser.io/edit.html ).

class Bomb extends Phaser.Physics.Arcade.Sprite {
    constructor(scene, x, y) {
        super(scene, x, y, '')
        scene.add.existing(this)
        scene.physics.add.existing(this)

        this.body.setSize(128, 128)
  }
}

class Player extends Phaser.Physics.Arcade.Sprite {
    constructor(scene, x, y) {
        super(scene, x, y, '')
        scene.add.existing(this)
        scene.physics.add.existing(this)

        this.body.setSize(32, 32)
  }

  plantBomb() {
    new Bomb(this.scene, this.x, this.y)
  }
}

var config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    scene: {
        preload: preload,
        create: create,
        update: update
    },
    physics: {
        default: 'arcade',
        arcade: {
            debug: true
        }
    }
};

var game = new Phaser.Game(config);

function preload ()
{
}

function create ()
{
    this.cursors = this.input.keyboard.createCursorKeys();
    this.player = new Player(this, 150, 150)
}

function update ()
{
    if (this.cursors.left.isDown)
    {
        this.player.setVelocityX(-400);
    }
    else if (this.cursors.right.isDown)
    {
        this.player.setVelocityX(400);
    }
    else
    {
        this.player.setVelocityX(0);
    }

    if (this.cursors.up.isDown)
    {
        this.player.setVelocityY(-400);
    }
    else if (this.cursors.down.isDown)
    {
        this.player.setVelocityY(400);
    }
    else
    {
        this.player.setVelocityY(0);
    }

    if (this.cursors.space.isDown) this.player.plantBomb()
}

Hi d0h,

I think it might be because you’re using .isDown.
If you add a console log to the plantBomb function it will probably of ran several times, creating several bombs and causing the flicker.

The solution could be to add this instead.
Phaser.Input.Keyboard.JustDown(this.cursors.space)
So now it should only create one bomb and hopefully stop the flicker.
From the docs: You can only call justDown once per key press. It will only return true once, until the Key is released and pressed down again.
https://photonstorm.github.io/phaser3-docs/Phaser.Input.Keyboard.html#.JustDown__anchor

Hope this helps :slight_smile:

Hey @retroVX thanks for the heads up about Phaser.Input.Keyboard.JustDown(this.cursors.space)

This is just a sample code. In the real app there is a logic that prevents from creating several bombs on the same tile. The problem is that the “flicker” actually has its own coordinates.

Here is the updated example

class Bomb extends Phaser.Physics.Arcade.Sprite {
    constructor(scene, x, y) {
        super(scene, x, y, 'bomb')
        scene.add.existing(this)
        scene.physics.add.existing(this)

        this.body.setSize(128, 128)
  }
}

class Player extends Phaser.Physics.Arcade.Sprite {
    constructor(scene, x, y) {
        super(scene, x, y, 'player')
        scene.add.existing(this)
        scene.physics.add.existing(this)

        this.body.setSize(32, 32)
  }

  plantBomb() {
    const bomb = new Bomb(this.scene, this.x, this.y)
    this.scene.bombs.add(bomb)
  }
}

var config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    scene: {
        preload: preload,
        create: create,
        update: update
    },
    physics: {
        default: 'arcade',
        arcade: {
            debug: true
        }
    }
};

var game = new Phaser.Game(config);

function preload ()
{
    this.load.image('player', 'assets/sprites/mine.png');
    this.load.image('bomb', 'assets/sprites/saw.png');
}

function create ()
{
    this.bombs = this.add.group()
    this.cursors = this.input.keyboard.createCursorKeys();
    this.player = new Player(this, 150, 150)
    this.player.setDepth(1);
}

function update ()
{
    
    if (this.cursors.left.isDown)
    {
        this.player.setVelocityX(-400);
    }
    else if (this.cursors.right.isDown)
    {
        this.player.setVelocityX(400);
    }
    else
    {
        this.player.setVelocityX(0);
    }

    if (this.cursors.up.isDown)
    {
        this.player.setVelocityY(-400);
    }
    else if (this.cursors.down.isDown)
    {
        this.player.setVelocityY(400);
    }
    else
    {
        this.player.setVelocityY(0);
    }

    if (Phaser.Input.Keyboard.JustDown(this.cursors.space)) this.player.plantBomb()

    this.bombs.children.iterate(child => {
        console.log(child.body.x, child.body.y)
    }) 
}

In this example you can see that when you click “Space” it logs 2 sets of coordinates, the first pair being from the “flickered” body

Hi, sorry for the late reply.

I tried the example by posting the code into the labs editor and it worked without any flickering as you can see from the gif. (I tried with debug on but it made no difference)

Is there any other code or anything? If not it could be a device problem but I’m not to sure.

@retroVX have you tried opening JS console and checking the output (according to my last code snippet)? It prints 2 sets of coordinates (first wrong, then correct) regardless of the debug on/off.

Here is the simplified example. Here you can see update() loop being run once per second. You can see in JavaScript console that 2 sets of coordinates are printed (wrong set is printed only once, afterwards everything is correct)

class Bomb extends Phaser.Physics.Arcade.Sprite {
    constructor(scene, x, y) {
        super(scene, x, y, 'bomb')
        scene.add.existing(this)
        scene.physics.add.existing(this)

        this.body.setSize(128, 128)
  }
}

class Player extends Phaser.Physics.Arcade.Sprite {
    constructor(scene, x, y) {
        super(scene, x, y, 'player')
        scene.add.existing(this)
        scene.physics.add.existing(this)

        this.body.setSize(32, 32)
  }

  plantBomb() {
    const bomb = new Bomb(this.scene, this.x, this.y)
    this.scene.bombs.add(bomb)
  }
}

var config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    scene: {
        key: 'testing',
        preload: preload,
        create: create,
        update: update
    },
    physics: {
        default: 'arcade',
        arcade: {
            debug: true
        }
    }
};

var game = new Phaser.Game(config);

function preload ()
{
    this.load.image('player', 'assets/sprites/mine.png');
    this.load.image('bomb', 'assets/sprites/saw.png');
}

function create ()
{
    this.bombs = this.add.group()
    this.player = new Player(this, 150, 150)
    this.player.setDepth(1);

    this.scene.pause();
    setInterval(() => {
        this.scene.resume()
    }, 1000)
}

function update ()
{
    // Only create 1 bomb
    if (!this.bombs.getChildren().length) this.player.plantBomb()

    const bomb = this.bombs.getFirst(true)
    console.log('Bomb body coordinates: ', bomb.body.x, bomb.body.y)

    this.scene.pause();
}

Hey,

I know this is a year after this was posted, but I ran into this problem as well. I’m on Phaser version 3.23.0.

I came up with a solution for it, and I wanted to post it just incase you or anyone else still needs it.
I modified your example and pasted it below.

For a quick explanation, I basically added a function named “arcadeSpriteFix()” at the bottom, and just call it after the sprite gets created in plantBomb. It fixes the flickering problem, and aligns the sprite’s body like intended.


class Bomb extends Phaser.Physics.Arcade.Sprite {
    constructor(scene, x, y) {
        super(scene, x, y, 'bomb')
        scene.add.existing(this)
        scene.physics.add.existing(this)

        this.body.setSize(128, 128)
  }
}

class Player extends Phaser.Physics.Arcade.Sprite {
    constructor(scene, x, y) {
        super(scene, x, y, 'player')
        scene.add.existing(this)
        scene.physics.add.existing(this)

        this.body.setSize(32, 32)
  }

  plantBomb() {
    const bomb = new Bomb(this.scene, this.x, this.y)
	this.scene.bombs.add(bomb)
	arcadeSpriteFix(bomb); //<-- add this line
  }
}

var config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    scene: {
        key: 'testing',
        preload: preload,
        create: create,
        update: update
    },
    physics: {
        default: 'arcade',
        arcade: {
            debug: true
        }
    }
};

var game = new Phaser.Game(config);

function preload ()
{
    this.load.image('player', 'assets/sprites/mine.png');
    this.load.image('bomb', 'assets/sprites/saw.png');
}

function create ()
{
    this.bombs = this.add.group()
    this.player = new Player(this, 150, 150)
    this.player.setDepth(1);

    this.scene.pause();
    setInterval(() => {
        this.scene.resume()
    }, 1000)
}

function update ()
{
    // Only create 1 bomb
    if (!this.bombs.getChildren().length) {
		this.player.plantBomb()
	}

    const bomb = this.bombs.getFirst(true)
    console.log('Bomb body coordinates: ', bomb.body.x, bomb.body.y)

    this.scene.pause();
}

//This is to fix the arcade sprite when the sprite is created in the update function instead of the create function.
//This basically realligns the sprite body's hitbox so it doesn't get out of sync with the sprite game object.
//You only need to call this once, and only after you create the sprite with this.scene.add.sprite(...);
//This also works with images. 
//If you need to set the size, scale, and offset of the body on creation, do it after you create the sprite and THEN call this function.
/*Ex:
	var s = this.scene.physics.add.sprite(50, 50, "spritesheethere", 0);

	s.body.setOffset(5, 5);
	s.setScale(2, 1);
	s.body.setSize(10, 19, false);

	arcadeSpriteFix(s);
*/
function arcadeSpriteFix(arcadeSprite) {
	var newx = arcadeSprite.x - (0.5 * arcadeSprite.displayWidth) + (arcadeSprite.scaleX * arcadeSprite.body.offset.x);
	var newy = arcadeSprite.y - (0.5 * arcadeSprite.displayHeight) + (arcadeSprite.scaleY * arcadeSprite.body.offset.y);

	arcadeSprite.body.position.x = newx;
	arcadeSprite.body.position.y = newy;
	arcadeSprite.body.prev.x = newx;
	arcadeSprite.body.prev.y = newy;
	arcadeSprite.body.prevFrame.x = newx;
	arcadeSprite.body.prevFrame.y = newy;
	arcadeSprite.body.transform.scaleX = arcadeSprite.scaleX;
	arcadeSprite.body.transform.scaleY = arcadeSprite.scaleY;
	arcadeSprite.body.width = Math.floor(arcadeSprite.body.width * arcadeSprite.scaleX);
	arcadeSprite.body.height = Math.floor(arcadeSprite.body.height * arcadeSprite.scaleY);
}

I’ll be honest, I actually experienced a different bug than the one you posted, and this function fixed my problem. When I applied it to your example, it fixed it as well. In my scenario, I was creating a sprite in the update loop (like you), but I was just doing “this.scene.physics.add.sprite(50, 50, “slime”, 0)”, and my bug was always moving my sprite.x/y to the upper left hand corner.

In my scenario, when the sprite gets created in the update loop, the sprite.x/y coordinates are initially correct (center coordinates of the sprite drawing), and the sprite.body.x/y coordinates are initially correct as well (top left corner of the rectangular body). However, the sprite.body.prevFrame.x/y are NOT correct (the previous frame’s top left corner of the rectangular body).

What happened is that the engine sees that the body’s prevFrame position is different than the current position, so it thinks that the body has moved. Because the engine sees that the body has moved, it applies the movement to the sprite.x/y as well. In the source code, it does this in src > physics > arcade > Body.js > postUpdate(). Here is the part of the function from the source code (Phaser version 3.23.0):

postUpdate: function ()  {
        var dx = this.position.x - this.prevFrame.x;
        var dy = this.position.y - this.prevFrame.y;

        if (this.moves)
        {
            ...

            this.gameObject.x += dx;
            this.gameObject.y += dy;
        }

        ...

    }

I’m not sure HOW the prevFrame got out of sync, but it did. So in my scenario, I just had to recalculate the position of the of the body based on the scale, offset, and size, and then apply the recalculation to the prevFrame. This is basically what the “arcadeSpriteFix()” function does.

Again, not exactly sure why it fixes your scenario too…but it does haha!

It’s fixed in v3.24.1.

Hey samme,

Ha! Thanks for letting me know!
I haven’t tried d0h’s example on the latest code until now (guess I should have checked that first).


TL;DR;
So idk…my post I made 9 hrs ago didn’t include the latest version. And the latest version fixed d0h’s problem. But, if you try to apply a size, offset, and scale to the sprite you create in the update function, there is still a frame where the hitbox is out of place. I have an example below.

My shitty little function technically fixes that too, but its kinda wierd to apply, and I can’t promise it won’t cause other bugs on the first frame of creation (like velocity not being applied because it resets the prevFrame, etc. I didn’t really test anything.). Idk…I just want to stop diving into this though, so I can actually USE the engine instead of finding bugs lol.


So, I tried d0h’s example on 3.24.1 on my localhost and on the online phaser sandbox (I just used an existing solution and copy+pasted his code in there. Like this one: https://labs.phaser.io/edit.html?src=src/animation/animation%20from%20png%20sequence.js&v=3.24.1). On the localhost I DID get the flicker, and on the sandbox version, I did DID NOT get the flicker. The only difference between my localhost and the online sandbox is that I did not have the actual texture “mine.png” and “saw.png”. So on my localhost, it used the “no texture” green box for the bomb, and on the online sandbox version, it used the actual bomb.

Turns out thats what made the difference. I downloaded the bomb and player texture to my localhost, and the flicker went away. I tried this same example with version 3.17.0 (d0h’s version when he first posted this: https://labs.phaser.io/edit.html?src=src/animation/animation%20from%20png%20sequence.js&v=3.24.1). I only tried it on the online sandbox version. I DID get the flicker, even with the properly loaded textures.

So yes, the version update definitely fixed it.

HOWEVER…and I hate to dig any deeper because I don’t want too…but there is still a frame where the hitbox gets out of sync with the sprite IF you apply a size, offset, and scale. Here is an example. Go to this link, and paste the code below and click run. https://labs.phaser.io/edit.html?src=src/animation/animation%20from%20png%20sequence.js&v=3.24.1.

class Bomb extends Phaser.Physics.Arcade.Sprite {
    constructor(scene, x, y) {
        super(scene, x, y, 'bomb')
        scene.add.existing(this)
        scene.physics.add.existing(this)

        //d0h's code - puts hitbox around entire buzzsaw - 0 frames where hitbox is out of place - GOOD
        //this.body.setSize(128, 128);

        //Miles's code - puts hitbox on bottom right corner of buzzsaw - 1 frame where hitbox is out of place - NOT GOOD
        this.body.setSize(64, 64, false)
        this.body.setOffset(64, 64);
        this.setScale(2,1);
  }
}

class Player extends Phaser.Physics.Arcade.Sprite {
    constructor(scene, x, y) {
        super(scene, x, y, 'player')
        scene.add.existing(this)
        scene.physics.add.existing(this)

        this.body.setSize(32, 32)
  }

  plantBomb() {
    const bomb = new Bomb(this.scene, this.x, this.y)
	this.scene.bombs.add(bomb)
	//arcadeSpriteFix(bomb); //<-- uncommment to fix the 1 frame where hitbox is out of place
  }
}

var config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    scene: {
        key: 'testing',
        preload: preload,
        create: create,
        update: update
    },
    physics: {
        default: 'arcade',
        arcade: {
            debug: true
        }
    }
};

var game = new Phaser.Game(config);

function preload ()
{
    this.load.image('player', 'assets/sprites/mine.png');
    this.load.image('bomb', 'assets/sprites/saw.png');
}

function create ()
{
    this.bombs = this.add.group()
    this.player = new Player(this, 150, 150)
    this.player.setDepth(1);

    this.scene.pause();
    setInterval(() => {
        this.scene.resume()
    }, 1000)
}

function update ()
{
    // Only create 1 bomb
    if (!this.bombs.getChildren().length) {
		this.player.plantBomb()
	}

    const bomb = this.bombs.getFirst(true)
    console.log('Bomb body coordinates: ', bomb.body.x, bomb.body.y)

    this.scene.pause();
}

//This is to fix the arcade sprite when the sprite is created in the update function instead of the create function.
//This basically realligns the sprite body's hitbox so it doesn't get out of sync with the sprite game object.
//You only need to call this once, and only after you create the sprite with this.scene.add.sprite(...);
//This also works with images. 
//If you need to set the size, scale, and offset of the body on creation, do it after you create the sprite and THEN call this function.
/*Ex:
	var s = this.scene.physics.add.sprite(50, 50, "spritesheethere", 0);

	s.body.setOffset(5, 5);
	s.setScale(2, 1);
	s.body.setSize(10, 19, false);

	arcadeSpriteFix(s);
*/
function arcadeSpriteFix(arcadeSprite) {
	var newx = arcadeSprite.x - (0.5 * arcadeSprite.displayWidth) + (arcadeSprite.scaleX * arcadeSprite.body.offset.x);
	var newy = arcadeSprite.y - (0.5 * arcadeSprite.displayHeight) + (arcadeSprite.scaleY * arcadeSprite.body.offset.y);

	arcadeSprite.body.position.x = newx;
	arcadeSprite.body.position.y = newy;
	arcadeSprite.body.prev.x = newx;
	arcadeSprite.body.prev.y = newy;
	arcadeSprite.body.prevFrame.x = newx;
	arcadeSprite.body.prevFrame.y = newy;
	arcadeSprite.body.transform.scaleX = arcadeSprite.scaleX;
	arcadeSprite.body.transform.scaleY = arcadeSprite.scaleY;
	arcadeSprite.body.width = Math.floor(arcadeSprite.body.width * arcadeSprite.scaleX);
	arcadeSprite.body.height = Math.floor(arcadeSprite.body.height * arcadeSprite.scaleY);
}

So, in Bomb’s constructor, If you run it with d0h’s code, everything works fine. However, when you try to run it with Miles’s code instead, there is 1 frame where the body is slightly out of place. If you run Miles’s code and run the sprite through arcadeSpriteFix(), then the 1 frame is fixed.

I have no idea if this is a problem worth looking into, and also like I said in the TL;DR, I just want to stop so I can continue working on my game lol. Its there if you want it though.

That’s expected, since the body is synced to the sprite transform (scale) only at the beginning of the game step.

The “wrong” hitbox shouldn’t be a problem unless you need to use it yourself during scene update(). The physics world step is already done at that point. The debug display makes it look like it’s been wrong for the whole frame, but it wasn’t.

I think there are two ways to solve the bomb problem:

  • In the Bomb constructor, do setScale(…) before adding the body; or
  • Call updateFromGameObject() after setScale().