Expectations from the Physics engine (Beginner)

Hello,

I’m learning Unity and I’ve started a small dynamics simulation of little objects moving randomly in space around an attractive object (imagine insects around a lamp). There a several parameters which can be changed in real time in the UI to modify the behaviour of the group. It’s looking good, group-wise (the objects themselves are simply small cubes).

This was scaling nicely from 10 to 1000 objects, flying around close together, no Physics involved. All calculations for random movement are done from within the cube class (you can check it out here if you like).

Problem is, I’ve went further and added a Rigidbody and BoxCollider to the objects, so that I can detect collisions between them. I only need the trigger information (i.e. I don’t want stuff to rebound around or use gravity). All of the objects are both isKinematic and isTrigger. I handle the trigger myself.

It works fine until there are many objects close together. When I bump the number of objects to 1000 and the attraction object’s to maximum, there are 1000 objects moving tightly in a very small space and I get around 2000 collision triggers per second.

I’ve read somewhere that Unity supports several hundred thousand rigidbodies per frame. And I’m very far from that.

  • Is Unity’s estimation so large because it refers to objects not being Kinematic (i.e. using the Physics engine for everything)?
  • My (recent) iMac is down to 5 FPS when I have 2000 collisions per second. Does that seem normal?
  • My code is beginner code, lots of random number generation and vector math (no square roots), plus probably lots of inefficient stuff. Is this something I should learn to fix with some compromises, or should I switch to Physics (the whole point of this project was learning about vectors in Unity).

Thanks a lot for your help.

Wherever you read that failed to put it in the proper context of things being spread out like what happens in most games. They’re considering “normal usage”, and you’re running the worst case scenario. As you’ve discovered, objects being close to each other makes that number plummet. This isn’t a failing of Unity or PhysX, but a fundamental fact of physics engines: when a bunch of stuff is close together, it’s gonna be slower.

For any physics system, it must check for collisions between every possible pair of object. If you have two rigidbodies, that’s 1 check. If you have 3, that’s 3 checks. If you have 10 rigidbodies, that’s 90 checks. It goes up fast. You have 1000 - that’s roughly 1,000,000 collision checks.

PhysX can do some clever things depending on the situation to reduce that. For one, it separates objects into groups based on position, into a sort of grid (it’s not really, and this is a gross oversimplification of the actual system, but imagine it is for illustration). If you have an object in cell D3, it only needs to check that cell and the neighboring cells (C3, D2, D4, E3, maybe the diagonals); so instead of checking against the other 999 objects, it only checks against the 20 or 30 in those cells. The same applies to all the other objects, so we’ve reduced 1,000,000 collision checks to around 20k-30k in that scenario. That’s a much more manageable number.

But then you brought these things together, into the same cell, as it were. There’s little or no optimization you can do in that scenario, so suddenly it’s doing all 1m collision checks again.

And when they start colliding, it gets worse. (Though, if your RB’s are triggers, this step is largely skipped.) A collides with B, and the engine works out the pushback on both objects. Not bad so far. Now C collides with B, and the engine has to calculate the pushback on not only B and C, but A as well (since they’re in contact). Throw in a few more objects, and every time one of them applies force, every other rigidbody has to figure out how it’s affected. This one is worse than the first collision detection problem, because while the original collision detections are each an independent calculation and can be multithreaded across all 4 of your CPU cores, the collision reactions/pushback each depends on the last one, and can’t really be threaded.

Plus, look at all the stuff it has to calculate for every single collision. That’s not cheap either.

Pretty bad, right? It gets worse.

Most slowdown is linear. If you have graphics that twice as long to render, your framerate will probably be halved. If your graphics took 8 ms per frame and then start taking 16 ms per frame, you’ll drop from 120fps to ~60fps. Not good, but not the end of the world.

Physics has to be calculated on a fixed timestep, otherwise collisions and reactions gets inconsistent. The default timestep in Unity is 0.02 seconds (20 ms), so there are 50 physics updates per second. Unfortunately, this framerate never slows down no matter what is going on in the scene. If physics starts getting heavy on the CPU, the game has no choice but to squeeze in all the other game processing around the physics processing. What this means is that if your physics frame which used to take 8 ms per frame starts taking 16 ms per frame, then the time available for the rest of the game - all your scripts, graphics, etc - drops from 12 out of every 20 ms, to 4 out of every 20 ms. Doubling your physics calculation time has, effectively, cut your framerate by 2/3. And if your physics frame takes 19 ms to calculate, you’ll cut your framerate by 11/12 - and you’ve barely more than doubled the amount of physics being calculated. And as we’ve seen, adding just a few objects (in your worst-case scenario) can do much more than double the processing time.

Unity, fortunately, puts a limiter on this in the extreme case, and starts to slow down time itself if things get too ridiculous (otherwise, a physics frame that takes 20 ms to calculate will make your game freeze entirely) - it forces itself to allocate at least some time for the main game thread before starting on the next physics frame no matter how far behind it is on physics. But the point here is that slowdown from physics is exponential. It’s exponential on top of being a squared value in the first place. And that’s how…

…that happens.

And no, nothing you write is going to really improve on this, at least not in the general use case. PhysX is optimized, native code that runs on multiple threads. It’d likely take you years (just like it took PhysX) to get close to its level of efficiency with large pools of objects.

Some things you can do to improve the efficiency:

  1. Increase the fixed timestep, especially if the framerate drops. This is found in Edit → Project Settings → Fixed Timestep, or in script via Time.fixedDeltaTime. This will reduce the accuracy of the physics and it’ll look a little choppier, but will alleviate the exponential problem somewhat.

  2. Separate things into layers. If you don’t need every object to collide with every other object, then you don’t need to check them. In Edit → Project Settings → Physics, there’s a grid with all of your layers, and you can uncheck the ones that don’t need to collide with each other.

  3. Use sphere colliders instead of cubes. Spheres are the fastest collider to calculate, and cubes are among the slowest (except for mesh colliders).

3 Likes

Now, I said that you wouldn’t be able to improve on PhysX in the general case, but there might be something that could be done in a specific case. If your objects are moving in circles all in the same direction, for example, they’re likely to have the same couple of neighbors most of the time. So, when you do a pass to calculate collisions between them, you can store their distances/references in a sorted list, and in the intermediate frames, you can look at only the nearest 3 or 4 objects for collisions. If you have a manager class, it can loop through your objects on a staggered basis so that only one or two of them in any given frame are doing a full refresh of their own lists of objects. You might miss a few collisions here or there if some of them are moving unpredictably, but if you’ve got thousands of objects, your player is unlikely to notice.

Thank you very much for this very detailed answer. The article that mentioned hundreds of thousands of rigidbodies is from the Unity blog (2014). I’ve used the Profiler to narrow down the problem and there are some optimisations I can already think of. As to your specific suggestions:

  • Tried it. Didn’t help.
  • Thought about it, but I can’t, it’s a swarm of insects, they’re all identical and should collide with each other.
  • Will definitely try that!

BTW, you mentioned the fixed time step but I think this is really for non-kinematic collider. I’m not using FixedUpdate anywhere, only Update and OnTriggerEnter.

Thanks again for your help and comments.

Hm… given this, I’m wondering if it might be faster if, instead of setting the position on your own, you set rigidbody.velocity and allowed the physics engine to handle that. It’s possible it does optimizations based on velocity, which wouldn’t be possible with you setting the position to (for all the engine knows) a completely arbitrary position.

Trying to figure out how best to exploit a heavily optimized system is a tricky beast! :slight_smile:

If you’re going that route then using MovePosition in FixedUpdate would probably involve less code refactoring. https://docs.unity3d.com/ScriptReference/Rigidbody.MovePosition.html

It’s also worth running the profiler. It’s possible your OnTriggerXXX code is slow too.

If you throw multiple GetComponent or Debug.Log calls, or something that generates high garbage, no optimisation of the physics engine will save you.

Don’t get fixated on solving one particular performance issue unless you have proven that’s your bottle neck.

1 Like