What is a collision pair, and how can I minimize them?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By jarlowrey

enter image description here

In game I am spawning 11 bullets every second and a new enemy every 2 s. The bullets are staying on screen, and the enemies I killed immediately. Thus everything should be the same layer/mask for most of the time, but collision pairs still increases, seemingly exponentially.

Why is that? Why doesn’t killing an enemy decrease the number of collision pairs more (especially since there is typically only 1 enemy on the screen at a time)?

For example, the following screen shot has 27.5k collision pairs, 26 fps, 3.1k nodes and 4k objects, but nothing has colliding layers/masks (the red stuff is not colliding). Why could that be?

enter image description here

I believe collision pairs is an algorithm term for two colliders being compared to see if they’re colliding. The simple version is that an object will create a list of neighbors to compare with one at a time, each pair would also allow the other object to avoid duplicating the collision check when it does its comparisons.

Without knowing more, your scene doesn’t look like it has thousands of nodes. You may have a leak somewhere.

If not, there is an option in Project Settings > Physics > 2d, for Cell Size.

A collision system will be exponentially slower with each addition, as each collider has to compare itself against every other. Each time you add one, that is one more that every collider has to test for. To optimize, Godot uses a grid, and colliders will only look within a certain area of that grid for neighbors to collision test. The objects that are far away in other grid cells can never be hit, and are ignored. Less pairs to tests.

When you have small objects that cluster, more can fit into a grid cell, resulting in more tests. So you may try experimenting with shrinking the grid cell size by small amounts.

avencherus | 2018-05-10 03:36

I have shrunk the grid size down to zero and it didnt seem to have much improvement. I’ve looked over the code and tested it (shoot mines, wait for them to be destroyed, destroy enemies, etc) to see if the nodes can reduce back down or if they keep increasing, and it seems to reduce itself (ie no leak). I will keep looking into ways to reduce node count, it does seem high but it may be right.

Thanks!

jarlowrey | 2018-05-10 05:38

Thus everything should be the same layer/mask for most of the time, but collision pairs still increases

Collision pairs only exist for things on the same layer/mask. For all the physics objects, what are their collision layers and masks set to be? are all your bullets set to collide with each other? That would explain the exponential-ish increase in pairs.

markopolo | 2018-05-11 16:11

What I mean is that in the screenshot with 27.5k pairs, nothing is set to collide with each other.

jarlowrey | 2018-05-12 14:02

Oh, shows what I know -_-

markopolo | 2018-05-12 17:14

Just my poor wording, no worries!

jarlowrey | 2018-05-14 23:30

:bust_in_silhouette: Reply From: jarlowrey

One year later, I’ve found the answer. Collision Pairs indicate how many objects are in the same grid cell for the 2D physics engine. These cells are used to determine whether there might be a collision between two objects.

I probably had so many collision pairs due to pooling objects and placing them all on top of each other. Now when I pool I randomly spread them out over a large area.

Unfortunately the area cannot be toooo large, otherwise the number of grid cells will increase, causing more performance hits.

You can also decrease the cell size in Project Settings > Physics > 2D > Cell Size, though there is a similar tradeoff

Hey man, I’m facing the same problem, have programming background so your answer can be technical.

I am not pooling as instancing is not a problem for performance in my case (yet - I’ll add pooling at the end). But when enemies get on top of each other, performance dips a lot. I’m also around 28k collision pairs.

Each enemy is an Area2D with a collision shape + 2 areas under it, a hurt_box and a hit_box (“attack box”). The masks are already setup so that only the main collision_shape is interacting with another enemy’s collision shape, hurt_box is only affected by player projectiles, and hit_box/attack_box only interacts with player’s hurt_box. I’m guessing the issue is that when 50+ enemies are near a player, that’s a lot of combinations between those 3 collision shapes and player’s own hurt_box and that explodes my Collision Pairs, is that correct?

I’m thinking of 2 possible solutions, would love your take on these:

  1. Easy solution: disable main Collision Shape on enemies when they’re close enough to the player. Those are used so enemies can knock other enemies away from themselves so they keep some minimum distance between them. However, once they’re close enough to the player, they’re already so bunched up that I suppose letting them just walk over each other is okay. Of course, once player moves away from bunched-up enemies, their collision shapes will re-enable and it will cause a spike, but… better a spike than constant lag.
  2. Hard and lower priority solution: if we have multiple enemies on top of player, disable hit_box of all enemies in that group but one, and increase the damage of that one enemy by sum of damage of all other enemies that touch the player. This is lower priority b/c, realistically, once enough enemies are on top of the player, the player should be dead. At the same time, you don’t want to lag in the final second of your life, and if you have invulnerability item that triggers, you can still escape so for those moments this optimization will be good.

Outside of reducing physics grid cell size, is there anything else I could realistically do here? (I already limit max enemy count on-screen to ).

Thank you so much!!

UPDATE: Ok, implementing #1 has solved all problems for now. Literally 1 line of code (collision_shape.disabled = distance_to_player < 100). Collision Pairs dropped by 10x down to ~2500 max. I think #2 solution isn’t necessary b/c there’s only 1 player, so every enemy’s attack_box/hit_box only pairs with 1 hurt_box (on the player), whereas with collision shape on enemies themselves we had each enemy creating a collision pair for each nearby enemy, which is ofc exponential growth in pairs. But if you have more wisdom, still would love to hear it!