Trouble with collision's between arcade groups (sometimes doesn't work)

Here’s a video of some misbehaving physics in my game -

There’s a couple of issues that can be seen in the video -

  • Zombies will “pop” in and out of each other (it’s possible for 100 zombies, to become ~10 due to stacking)
  • Zombies will “push” each other through the wall, when they are stacked next to each other

I’m using the arcade physics system (run @ 60FPS, and 120FPS with similar results). My zombies are arcade groups, as are my walls.

I collide each zombie (or rather the group) of them, with a group of walls, and with each other as such -

      this.scene.physics.add.collider(this.botGroup, this.worldLayer, (bot: BotSprite, enemy: any) => {
        //  NO LOGIC FOR NOW
      });

      this.scene.physics.add.collider(this.botGroup, this.botGroup, (bot: BotSprite, enemy: any) => {
        //  NO LOGIC FOR NOW
      });

I make use of the preUpdate method within each bot sprite, to set the velocity

      // In this context, "this" is the bot sprite within the group
      const targetPlayer = this.playerSystem.getClosestAliveClient(this.x, this.y);
      this.scene.physics.moveToObject(this, { x: targetPlayer.x, y: targetPlayer.y }, this.speed);

      // Also tried just setting velocity in the direction of the player, to the same effect.

Is there a way to stop this? I’ve tried changing their bounce, weights, and all other sorts of stuff, any help much appreciated!

Try setting body.pushable = false on each zombie.

Thanks for the tip @samme. It’s weird, it’s not recognised as a valid property on the body (but seems to exist in the docs Phaser 3 API Documentation - Class: Body).

I ignored the typescript warning and tried it anyway, but no luck unfortunately! Zombies still stack, and go through walls. Here’s my zombie sprite if it helps -

export class BotSprite extends Phaser.Physics.Arcade.Sprite {
  speed: number
  associatedEntityId: number
  health = 100
  onhandleBotDeathCallback: BotDeathCallback
  handleBotDamageCallback: BotDamageCallback

  onCreate(
    handleBotDeathCallback: BotDeathCallback,
    handleBotDamageCallback: BotDamageCallback,
  ): void {
    this.disableBody(true, true);

    this.onhandleBotDeathCallback = handleBotDeathCallback;
    this.handleBotDamageCallback = handleBotDamageCallback;
  }

  spawn(
    associatedEntityId: number,
    startX: number,
    startY: number,
  ): void {

    this.enableBody(true, startX, startY, true, true);

    this.associatedEntityId = associatedEntityId;
    this.health = gameConfig.zombies[this.botType].health;

    this.setSize(16, 16);

    // This isn't recognised - but I can see it in docs? https://photonstorm.github.io/phaser3-docs/Phaser.Physics.Arcade.Body.html#pushable__anchor
    // @ts-ignore
    this.body.pushable = false;

    this.speed = randomIntFromInterval(gameConfig.zombies[this.botType].minSpeed, gameConfig.zombies[this.botType].maxSpeed);
  }


  public moveBot(nextPath: { x: number; y: number }): void {
    const nextPathTileX = nextPath.x;
    const nextPathTileY = nextPath.y;
    this.scene.physics.moveToObject(this, { x: nextPathTileX, y: nextPathTileY }, this.speed);
  }
}

And here’s my wall if it helps -

export class BuildingBlockWallSprite extends Phaser.Physics.Arcade.Sprite {

  public health = 100
  public associatedEntityId = -1
  onHandleWallDestroyedCallback: BuildingDestroyedCallback

  spawn(
    x: number,
    y: number,
    associatedEntityId: number,
  ): void {

    logger.debug("Spawning ACTUAL WALL :D");
    this.enableBody(true, x, y, true, true);
    this.associatedEntityId = associatedEntityId;
    this.health = 100;

    // this.setSize(50, 50);
    this.setSize(32, 32);
    this.setDisplaySize(32, 32);

    this.type = "WALL";
  }

  create(onHandleWallDestroyedCallback: BuildingDestroyedCallback): void {
    this.disableBody(true, true);
    this.body.immovable = true;
    this.onHandleWallDestroyedCallback = onHandleWallDestroyedCallback;
  }

  public takeDamage(): void {
    this.health -= 0.01;

    if (this.health <= 0) {
      logger.debug("Wall Destroyed");

      this.onHandleWallDestroyedCallback(this.associatedEntityId);
      this.disableBody(true, true);
    }

  }

}


In Phaser v3.50 and after the body.pushable property should exist.

Other things you can try:

  • Put the wall collider last
  • Do moveToObject() etc. in an update method instead of preUpdate
  • Increase overlapBias
  • Increase physics FPS again

So I tried all of those with no luck :frowning:

  • Wall collider with all zombie groups, are defined last in my BotSystem file

    { // ------- NORMAL ZOMBIES
      this.botGroup = this.scene.add.existing(
        new Bots(this.nengiInstance, this.onBotDeathCallback, this.onBotDamageCallback, navMesh, this.playerSystem, ZombieTypes.STANDARD_ZOMBIE, this.scene.physics.world, this.scene, { name: ZombieTypes.STANDARD_ZOMBIE })
      ) as unknown as Bots;

      this.botGroup.createMultiple({
        key: "zombie",
        quantity: config.zombies[ZombieTypes.STANDARD_ZOMBIE].maxCount,
        active: false,
        visible: false
      });

      this.scene.physics.add.collider(this.botGroup, this.worldLayer, (bot: BotSprite, enemy: any) => {
        //  NO LOGIC FOR NOW
      });

      this.scene.physics.add.collider(this.botGroup, this.botGroup, (bot: BotSprite, enemy: any) => {
        //  NO LOGIC FOR NOW
      });
    }

    { // ------- FAST ZOMBIES
      this.fastZombieBotGroup = this.scene.add.existing(
        new Bots(this.nengiInstance, this.onBotDeathCallback, this.onBotDamageCallback, navMesh, this.playerSystem, ZombieTypes.FAST_ZOMBIE, this.scene.physics.world, this.scene, { name: ZombieTypes.FAST_ZOMBIE })
      ) as unknown as Bots;

      this.fastZombieBotGroup.createMultiple({
        key: ZombieTypes.FAST_ZOMBIE,
        quantity: config.zombies[ZombieTypes.FAST_ZOMBIE].maxCount,
        active: false,
        visible: false
      });

      this.scene.physics.add.collider(this.fastZombieBotGroup, this.worldLayer, (bot: BotSprite, enemy: any) => {
        //  NO LOGIC FOR NOW
      });

      this.scene.physics.add.collider(this.fastZombieBotGroup, this.fastZombieBotGroup, (bot: BotSprite, enemy: any) => {
        //  NO LOGIC FOR NOW
      });
    }
    { //  ------- HELL ZOMBIES

      const navMeshHellZombies: NavMesh = new NavMesh(navMeshPolygonPoints, 32);
      this.hellZombieGroup = this.scene.add.existing(
        new HellZombieGroup(this.nengiInstance, this.onBotDeathCallback, this.onBotDamageCallback, navMeshHellZombies, this.scene.physics.world, this.scene, this.worldLayer, { name: ZombieTypes.HELL_ZOMBIE })
      ) as unknown as HellZombieGroup;

      this.hellZombieGroup.createMultiple({
        key: ZombieTypes.HELL_ZOMBIE,
        quantity: config.zombies[ZombieTypes.HELL_ZOMBIE].maxCount,
        active: false,
        visible: false
      });

      this.scene.physics.add.collider(this.hellZombieGroup, this.worldLayer, (bot: BotSprite, enemy: any) => {
        //  NO LOGIC FOR NOW
      });

      this.scene.physics.add.collider(this.hellZombieGroup, this.hellZombieGroup, (bot: BotSprite, enemy: any) => {
        //  NO LOGIC FOR NOW
      });
    }


    this.scene.physics.add.collider(this.buildSystem.buildingBlockLowWallsGroup, [this.botGroup, this.hellZombieGroup, this.fastZombieBotGroup], (buildingBlock: BuildingBlockWallSprite, bot: BotSprite) => {
      // logger.debug("Bot hit wall");
      buildingBlock.takeDamage();
    });

  • I converted my Bot sprite to use update by adding this.runChildUpdate = true; to the group constructor, same behaviour still though
  • Increasing overlapBias results in the zombies jumping around and through each other and through collideables even more
  • Tried up to 250 FPS, with no difference

If you remove the botGroup vs. botGroup collider does it make a difference?

Every single zombie collides with the wall correctly in that case (they never pop through), however they obviously then all just stack on top of each other. It seems to be an issue with the bots “merging” and “popping” out of each other, pushing them through other collideables.

Attached a video for when the standard bots don’t have colliders (I kept them on green, as you can see just a small amount of them forces this issue where they stack & push each other through collideables) -

I tried a few things and only raising physics FPS seemed to work. Preventing tunneling was easier than preventing packing.