I am working on a 2D side scroller in which most of the enemies are in a patrol-state at the start and then, if the player gets near them, enter a chase- or battle-state. I am currently thinking about different methods to detect if a player is near an enemy:
The first approach is to give every enemy a CircleCollider2D which is a trigger to detect the player. (The Physics2D-Layers are arranged so that only the player would cause OnTriggerEnter2D() to be called.) Then in OnTriggerEnter2D(), the player is registered as found and the chase-state is entered. But it seems uneccessary to me, to check for the player every single frame. Once or twice a second would be enough for me, so I am thinking about doing something like:
- Creating a method LookForPlayer() which uses Physics2D.OverlapCircle() to detect the player and if the player is found, calls a callback function (so that callback kind of replaces OnTriggerEnter2D()).
- Make this method being called repeatedly by using something like InvokeRepeating(“LookForPlayer”, 0.5f, 0.5f).
Is there any performance advantage in my second suggested method (because the physics engine needs to do the detection only twice per second instead of every frame) or would the performance maybe even be worse, becaus InvokeRepeating() causes some overhead and Colliders/Triggers are highly optimized for such cases anyway and thus better than using Physics2D.OverlapCircle() manually?
Maybe someone has experience with that or possibly a great understanding of the engine, so they know about the mentioned possible performance differences? I know I could of course run my own “benchmark” for this, but I wanted to ask for experiences first before doing uneccessary work…
If depends heavily on which is your target platform, the amount of enemies in your scene and such, so yeah, running your own benchmark would be your best option, but from my understanding of Unity’s 2D physics and general physics in game:
- If you are using Unity’s colliders and your objects do move, attach an Rigidbody to that object (or Rigidbody 2D in this case) and let isKinematic checked if you don’t need the physics to move. Static triggers don’t need them, as they are “baked” in their position.
- You can always adjust the Fixed Time Step to adjust how frequent the physics needs to be updated.
- Half a second can cause an “lag-feel” in your game depending on what you are doing.
- As I mentioned Fixed Time Step, it is good to remember that the physics are not updated “every frame”.
- With the Rigidbody, you have the option to put that object to “Sleep”, so you can improve your game performance later if needed (as enemies that are too far away don’t need to be updated anymore.
- If your map is too big, you can have multiple scenes in your game and loading them as you needed (take a look around about level streaming if you don’t know what I am talking about).
- If targeting mobile, level streaming usually is not a good option, as having a huge amount of colliders and calling Physics2D.OverlapCicle() in a huge amount of objects too (having a huge amount of objects using physics at same time is not a good option at all in mobile devices).
- If you are using Unity’s physics layers, remember that you are already optimizing a lot the whole system, as you are already removing unnecessary OnTriggerEnter calls, while OverlapCircle would be called anyway.
Summing up, the Unity’s collider are very good and already provides a lot of optimization options, so I do not see why not to use them. In general, using triggers is the best option as, even if having performance issues, you can always optimize it by yourself, as using the InvokeRepeating to disable objects too far away or to put their rigidbodies to sleep.
Sorry if my answer wasn’t clear enough.
A very important note here,
is that for 2d, unity explicitly requires that you move objects using rigidBody.movePosition and not transformPosition,
otherwise it causes a big performance issue (physics engine kicks in for some reason, and the function to get contacts is called frequently)