Managing performance with many gameobjects

Hello!

In a space game I am making (3D), I have a system that places asteroids to simulate an asteroid field. The optimal density is around 250 asteroids to 1000 metres cubed. The only problem is that this is not very performant!
Whenever I have many asteroids in the scene, the fps drops down to around 40 or 50.
Even worse, I intentionally made it so that when the player’s missiles hit an asteroid, it “breaks apart.” This means that it destroys itself and instantiates a replica of itself made up of many different pieces. These pieces can be pushed around by rigidbodies like normal. And there are about a hundred pieces per asteroid. That is a LOT to simulate and render!

Skipping to the point, I disabled collisions between asteroids and still whenever I face all the asteroids at once my fps drops down below 20 and sometimes as low as 9. Even if I face the other way, it’s still below 40.

I’ve heard that using ECS/Jobs/Burst can speed up using many gameobjects at once, but I’ve never tried it. Would it help? Would anything help? Or maybe I just can’t make an asteroid field?

Many thanks,
Arcane Creator

For all performance and optimization issues, ALWAYS start by using the Profiler window:

Window → Analysis → Profiler

Generally optimization is:

  • avoid doing the thing that is slow (easiest)
  • do it fewer times and store its result
  • do the slow thing less frequently over time
  • do the slow thing when nobody cares much (during level loading or in another thread, etc.)
  • find a faster way to do the thing (hardest)

DO NOT OPTIMIZE “JUST BECAUSE…” If you don’t have a problem, DO NOT OPTIMIZE!

If you DO have a problem, there is only ONE way to find out: measuring with the profiler.

Failure to use the profiler first means you’re just guessing, making a mess of your code for no good reason.

Not only that but performance on platform A will likely be completely different than platform B. Test on the platform(s) that you care about, and test to the extent that it is worth your effort, and no more.

Do not blindly reach for multi-threading or async… understand your performance problems first:

Remember that you are gathering information at this stage. You cannot FIX until you FIND.

Remember that optimized code is ALWAYS harder to work with and more brittle, making subsequent feature development difficult or impossible, or incurring massive technical debt on future development.

Don’t forget about the Frame Debugger window either, available right near the Profiler in the menu system.

Notes on optimizing UnityEngine.UI setups:

When you are investigating a performance concern, you may even need to create custom tooling or scripts to clearly expose the issue and help you reason about possible solutions. This may even involve making special instrumented builds of your game capable of running on the actual problematic target hardware.

At a minimum you want to clearly understand what performance issues you are having:

  • running too slowly?
  • loading too slowly?
  • using too much runtime memory?
  • final bundle too large?
  • too much network traffic?
  • something else?

If you are unable to engage the profiler, then your next solution is gross guessing changes, such as “reimport all textures as 32x32 tiny textures” or “replace some complex 3D objects with cubes/capsules” to try and figure out what is bogging you down.

Each experiment you do may give you intel about what is causing the performance issue that you identified. More importantly let you eliminate candidates for optimization. For instance if you swap out your biggest textures with 32x32 stamps and you STILL have a problem, you may be able to eliminate textures as an issue and move onto something else.

This sort of speculative optimization assumes you’re properly using source control so it takes one click to revert to the way your project was before if there is no improvement, while carefully making notes about what you have tried and more importantly what results it has had.

“Software does not run in a magic fairy aether powered by the fevered dreams of CS PhDs.” - Mike Acton

2 Likes

An asteroid field is almost classic test case for when you need to stop using gameobjects.

A lower-level API to explore is the Batch Renderer Group.

Render a large number of environment objects where using individual GameObjects
would be too resource-intensive. For example, procedurally-placed plants or rocks.

You could use the particle system for the asteroids.

Thanks for your answer.

Would physics still work on induvidual levels with a particle system?

Thanks for your answer.

The Batch Renderer Group doesn’t work with the built in render pipeline. Is there anything else I can use?

Thanks for your answer.

I did use the Profiler and my best informed guess is that rendering all the objects takes the most time, while collisions is a close second. I was looking at ECS hoping there is a way to cut down on render time when using the same object multiple times.

My second reason for thinking about DOTS is that I was only using one asteroid field. My ideal scenario would have about three times as many.

Just so you know, I am building for Windows so I don’t have to worry about testing with Mac or Linux.

what about gpu instancing

Yep, each asteroid particle can collide with other objects and can be made to spawn more asteroid particles upon collision, all without the need to script anything.

Thanks for your answer, it seems the best solution.
I tried making a particle system with the asteroids’ mesh, material and texture as particles but it’s even less performant than without a particle system. Any tips?

Extremely aggressive culling and LOD is probably going to be the easiest answer.

Also, remember that high speeds are generally really problematic for computer game physics systems. Keep your quantities within 10:1 of each other, certainly no more than 100:1

BTW, if you want to fly my 3D first person asteroids thingy (no shooting, just crash into them and send them spinning), check out the Spaceship3D in my Proximity Buttons project.

proximity_buttons is presently hosted at these locations:

https://bitbucket.org/kurtdekker/proximity_buttons

Jobs and Burst can speed up simulation of custom game logic. They won’t speed up your physics, and they won’t speed up your rendering. GPU Resident Drawer would be the thing to move to if you insist on using GameObjects. Or you use a custom instancing package (there’s plenty of them out there).

ECS doesn’t speed up GameObjects, it outright replaces them with an entirely new architecture that the rest of your game will need to be rebuilt around. And it comes with a fairly steep learning curve (although space games are one of the easier genres to learn ECS with) and requires a detail orientation that not everyone is capable of.

With that said, ECS will open up a lot of optimization opportunities. It may not give you immediate gains, but a lot of us will be able to help you read profiler captures and provide hints on how you can achieve them.

Really, I think you have mainly two separate problems. I suspect your poly count might be too high (especially if you are using GPU Resident Drawer) and you should probably use LOD Groups. As for collisions, you might need to implement custom collision detection with jobs and Burst. I have a framework for ECS that helps a lot with that.

3 Likes

Be sure to use a material that’s intended for particles. So try a material that uses the Particles/Standard Surface shader. And make sure your asteroid mesh isn’t too high poly.

BTW - all suggestions in this thread are great. You’ll probably have to make some compromises regardless of which option you choose. And some suggestions are obviously going to take longer to learn and implement.

Thank you all for your input, I will either use gameobjects and try cull them or use lower poly meshes, or a particle system and figure that out, or learn how to implement ECS.

1 Like