Best practice for large amounts of gameobjects

I am making a 2D dungeon crawler which has pretty large levels. This means that although there will rarely be many objects on the screen at any given time, the entire world has quite a lot of creatures, and I am starting to see serious slowdowns. Specifically caused by a large number of gameobjects tagged as “enemy”

Each enemy object has a collider and a rigidbody, as well as some simple scripts with logic in the update method. All of which can safely be ignored when not on screen.

To make sure Unity does not spend any time on anything that is not visible, in particular on collision and physics logic which I think is the main culprit here, I was trying something like this from the main game loop, set to execute as the first script on each frame redraw:

GameObject [] enemies = GameObject.FindGameObjectsWithTag ("enemy");
foreach (GameObject go in enemies) 
    go.SetActive(go.renderer.isVisible);

However, that does not work since disabled objects wont be collected during the next pass using FindGameObjectsWithTag, and as they are disabled, I suppose the rendererer wont even check if they are potentially visible or not. And, I am not sure if this approach is good practice anyway…

How to best make sure Unity does not spend any time on objects that are not on screen?

I solved this by implementing my own occlusion culling system. The trick was to manually keep a reference to all objects I set to inactive in a list, as the GameObject find methods will not return those anymore.

Then for each frame I iterate through both active and inactive objects and check if they are in the camera view frustum depending on their coordinates and size, setting their status accordingly and updating the list of inactive objects.

The result is an immense improvement in performance, as this method will block any CPU hogging behavior from invisible objects, such as physics and script updates. Only with a small penalty of manually doing the occlusion culling.

I think occlusion culling is prety much what you are looking for.

You may also find use for the OnBecameVisible and OnBecameInvisible methods. I think setting the rigidbodies to kinematic in OnBecameInvisible would help, possibly disable them along with the movement scripts. Set everything back in OnBecameVisible.

I hope this will help alleviate your issues.

Keep in mind, that the Scene View Camera in the Editor will also call these methods.