Arcade Physics Collision Categories

Hello, I’m using Phaser’s Arcade Physics and working with collision categories. My goal is to control how different units (players and enemies) collide in various scenarios. Specifically, I want to:

  1. Enable collisions between all units (players vs. players, enemies vs. enemies, and players vs. enemies) by default.
  2. Disable collisions between friendly units (e.g., player vs. player or enemy vs. enemy) when specific units are “firing” or performing certain actions.
  3. Restore friendly collisions when the units return to a different state (e.g., melee combat).

Here’s how I currently have everything set up:

Scene Setup

In the scene constructor, I define collision categories using this.physics.nextCategory():

this.categories = {
  enemy: this.physics.nextCategory(),
  player: this.physics.nextCategory(),
};

I also create Arcade Physics groups for the different unit types:

context.physicsGroups = {
  enemyGroups: context.physics.add.group(),
  playerGroups: context.physics.add.group(),
  enemyBaseZones: context.physics.add.group(),
  playerBaseZones: context.physics.add.group(),
  arrows: context.physics.add.group(),
};

Then, I add colliders for different scenarios:

context.physics.add.collider(
  context.physicsGroups.playerGroups,
  context.physicsGroups.enemyGroups,
  handleOpponentCollision,
  undefined,
  context
);

context.physics.add.collider(
  context.physicsGroups.enemyGroups,
  context.physicsGroups.enemyGroups,
  handleFriendlyCollision,
  undefined,
  context
);

context.physics.add.collider(
  context.physicsGroups.playerGroups,
  context.physicsGroups.playerGroups,
  handleFriendlyCollision,
  undefined,
  context
);

In the constructor for each unit, I assign the collision category dynamically based on the unit’s side (player or enemy):

if (this.info.unitSide === "enemy") {
  this.body.setCollisionCategory(this.scene.categories.enemy);
} else {
  this.body.setCollisionCategory(this.scene.categories.player);
}
console.log(this.body.collisionCategory); // Verifying category assignment

// By default, allow collisions with both categories
this.body.setCollidesWith([this.scene.categories.enemy, this.scene.categories.player]);

When the unit enters a “firing” state, I disable friendly collisions:

this.body.removeCollidesWith(
  this.info.unitSide === "enemy" ? this.scene.categories.enemy : this.scene.categories.player
);

When the unit returns to melee combat, I restore collisions with friendly units:

this.body.setCollidesWith([this.scene.categories.enemy, this.scene.categories.player]);

The Problem

While testing, I noticed that enemy units (Category X) and player units (Category Y) no longer collide with each other. They completely ignore each other, even though the categories are set correctly.

Here are some additional details:

  • Enemy units have a collision category X and player units have category Y.
  • I’ve verified the categories are being assigned correctly by logging this.body.collisionCategory.
  • When collisions stop, neither handleOpponentCollision nor handleFriendlyCollision callbacks are triggered.

My Understanding of the Problem

From the Phaser documentation, Arcade Physics supports setCollisionCategory(), setCollidesWith(), addCollidesWith(), and removeCollidesWith(). And I don’t want to use Matter physics.

However, something is causing the collisions to break entirely, and I can’t figure out why.

My Question

  1. Is there something I’m missing in how Arcade Physics handles collision categories?
  2. Are there known limitations or specific configurations required to make these methods work with Arcade Physics?
  3. Does this approach look correct, or should I manage this differently?

Any guidance would be greatly appreciated. Thank you!

Does the problem also happen if you remove the firing state switch?

You can put a breakpoint in Phaser.Physics.Arcade.World#canCollide and see what’s going on.

Hi. The same problem happens as if the arcade physics would not support categories.

If you can make a minimal test case, please make a bug report.