How to Properly Handle Overlaps Within a Group

I am working on a Shmup written in Phaser 3. As you might expect, I have lots of different types of enemies, bullets, and effects. For organizational purposes, each type of object has its own group to serve as a pool. Originally, I had set up colliders based on each pool and used metadata to figure out which pools needed colliders with which other pools. This -worked-, but even with a process callback that filtered out inactive objects the colliders running for all of the pooled objects slowed the game down significantly.

To fix this, I created a new phaser group named this.active. Now whenever I spawn an object, I add it to this.active and remove it from this.active group when it is despawned. This.active has a single collider that runs all of the time, with the metadata to sort which objects should and should not collide being applied inside the process callback. This has fixed my performance issues, but it has introduced a separate problem: the only way I know how to introduce a collider on elements of the same group is with:

this.physics.add.overlap(this.active, this.active, HandleOverlap, ProcessOverlap);

The problem with this is that it treats this as two separate groups, i.e. if there are n elements in this.active, it runs n^2 tests. This means each pair of objects is tested twice, and that everything seems to be tested against itself. Is it possible to set up a collider that will treat this as a collider on a single group, i.e. ignores duplicate pairs and runs (n-1)(n)/2 tests? Performance-wise the extra tests are not a huge problem, the bigger issue is that things like weapon damage or power up bonuses are now calculated twice per frame, doubling the effect of everything.

I don’t think so :frowning: . When using the collision tree I think it’s not simple to detect duplicate pairs. You could write your own loops for this and call collideSpriteVsSprite(…).

Re. your original method, I would try:

  1. Use physics.world.disableBody(body) etc. instead of sprite.disableBody() etc. This way disabled bodies are never searched or processed.
  2. Overwrite PhysicsGroup#contains() with your own simple test (matching a sprite to its pool).
1 Like

I started to write my own collider to handle this use case, but as I was looking through the documentation for how to do it, I found one place where it already exists. The World.collideObjects method will do the proper “triangular” version of the colliders if the first object type is an array and the second is null. So I just needed to change this to:

this.physics.add.overlap(this.active.children.entries, false, HandleOverlap, ProcessOverlap);

Edit for clarity: The .children.entries gets the array underlying a group. If you just pass in the group, the World will use the GroupVsGroup handler which just does the full “square” check.

If the collision tree is on (the default), I don’t think each group member is intersected with every other member. Instead there’s one tree search per group member. There are still duplicate pairs, though.

I’m not super well-versed in how Phaser’s tree is implemented, but all of my pooling objects are located in the same position in space so the vast majority of them should have been close enough at most times to trigger the in-depth collision checks. I was only setting active and visible to false rather than disabling the body entirely.