Summary
Detecting collisions between a Phaser physics group (bullets) and a tilelayer requires the tilelayer to have Arcade physics enabled and the tiles to be marked as collidable. The most common pitfall is mixing Arcade physics with Matter, or forgetting to set the tilemap layer’s collision index after the layer is added to the physics world.
Root Cause
- The tilelayer was added to the scene but not registered with the Arcade physics system (
this.physics.add.existingis missing). setCollisionByPropertyonly flags tiles internally; it does not automatically create Arcade bodies for the layer.- The collider was created before the layer had any physics bodies, so the callback never fired.
Why This Happens in Real Systems
- Layer creation order: Developers often create the collider immediately after the layer, assuming the layer is already a physics object.
- Mixed physics engines: Projects that use both Arcade and Matter can accidentally instantiate the tilemap with the wrong system.
- Dynamic tile updates: When tiles are added or changed at runtime, their collision flags must be refreshed, otherwise the physics world sees an empty body.
Real-World Impact
- Bullets pass through walls, leading to unfair gameplay and wasted server resources (extra bullets lingering).
- Players report “hit detection invisible” bugs, increasing support tickets.
- In competitive titles, the flaw can be exploited for map‑hacking or unintended shortcuts.
Example or Code (if necessary and relevant)
// Create the tilemap and layer
const map = this.make.tilemap({ key: 'level' });
const tileset = map.addTilesetImage('tileset', 'tiles');
const groundLayer = map.createLayer('Ground', tileset, 0, 0);
// Enable Arcade physics on the layer
this.physics.world.enable(groundLayer);
groundLayer.setCollisionByProperty({ collides: true });
// Create a bullets physics group
this.bullets = this.physics.add.group({
classType: Bullet,
runChildUpdate: true
});
// Add collider AFTER the layer has physics bodies
this.physics.add.collider(this.bullets, groundLayer, (bullet, tile) => {
console.log('hit');
bullet.setActive(false);
bullet.setVisible(false);
bullet.body.enable = false;
});
How Senior Engineers Fix It
- Verify physics system: Ensure the tilemap layer is created with
this.physics.add.existingorthis.physics.world.enable. - Set collision after layer creation: Call
setCollisionByPropertyafter the layer is added to the physics world. - Use consistent groups: Keep bullets in an Arcade physics group; avoid mixing with Matter bodies.
- Debug with visual helpers:
this.physics.world.createDebugGraphic()orthis.physics.world.drawDebug = trueto see whether bodies exist. - Refresh collisions on runtime: If tiles change, re‑run
setCollisionByPropertyand callgroundLayer.refresh().
Why Juniors Miss It
- Assume convenience methods are magical: They think
setCollisionByPropertyautomatically creates physics bodies. - Overlook order of operations: They place the collider call before the layer is fully initialized.
- Mixing APIs: Junior developers may copy code from Matter examples into an Arcade project, causing silent failures.
- Lack of debugging habits: Without visual debug graphics, they don’t notice that the layer has zero bodies.