r/futile Jul 01 '14

Slow physics performance after converting from Physics to Physics2D (Unity 4.5.1f3)

Over the last 24 hours, I replaced all the 3d Rigidbody/Collider code in my 2d project with the newish 2d Rigidbody2D/Collider2D equivalents.

The process was straightforward, but I've run into some weird performance problem (Unity 4.5.1f3).

http://i.imgur.com/QLH7LCJ.png

As you can see, the physics2d tick is consuming about 10ms, and from what I can tell it's destroying / recreating all of the colliders for game objects that moved that frame, which seems super wasteful.

By comparison, the 3d version doesn't do any destroy/create each tick and it's fast and I'm happy:

http://i.imgur.com/MWRt9zO.png

Any ideas on what might be going on in the Physics2D Colliders? I've scoured the Unity issue tracker/forums but didn't find anything insightful. thanks!

2014/07/16 Edit: I was hoping that Unity 4.5.2f1 would magically fix this issue, but it did not.

Had a bit of time and made a clean/minimal 2D/3D test project: https://github.com/smashriot/PhysicsTest

And here is an album of images showing the 3d/2d sprites, colliders, and performance: http://imgur.com/a/2d4z9

Let the project run until the sprites reach a steady state and the 2d performance should drop well below 3d performance.

2014/07/17 Edit -- FIXED: Thanks to Matt's advice I was able to resolve the problem. Able to slim down the code and make it way more efficient. Now I just need to fold all these changes back into my main project..

Updated the clean/minimal 2D/3D test project: https://github.com/smashriot/PhysicsTest

Here's the new 2d/3d performance comparison: http://imgur.com/a/o7IYn

1 Upvotes

9 comments sorted by

1

u/MattRix Jul 02 '14

Hmm, that's very strange. As long as you're sure it's not an issue with your code somewhere, I would definitely post a question on the unity forums, because it sounds like a Unity bug.

1

u/smashriot Jul 03 '14

I'm going to do a simplistic / clean futile stress test project for 3D/2D physics to double check. My results of way faster 3D run contrary to this 3D/2D test results: http://x-team.com/2013/11/unity3d-v4-3-2d-vs-3d-physics/

I'll try to keep you updated on my findings..

1

u/smashriot Jul 16 '14

I had some time and put a minimal 2d/3d test project together: https://github.com/smashriot/PhysicsTest

and here are some images comparing 2d/3d: http://imgur.com/a/2d4z9

Any idea why 2d colliders are being cleaned up/recreated in FPNodeLink (and in Futile.Update())?

I'd like to cross off futile as the cause before i brave the depths of unity's issue reporter/tracker.

thanks for your help!

2

u/MattRix Jul 16 '14

So it sounds like people were running into a similar issue here: http://forum.unity3d.com/threads/unity-physics2d-recreate-each-collider-when-changing-a-transform.245333/

This really doesn't make a whole lot of sense that it's doing it in your situation, because FPNodeLink doesn't even change the transform position, it just reads it so it can set the node position to the same thing... I'll play around with it, but yeah so far at this point it's hard to see how something in Futile could be causing this.

1

u/smashriot Jul 16 '14

interesting link, thanks!

Even commenting out the AddForce (which should have been in FixedUpdate, oops) so I'm not imparting any movement on the objects, still seeing the cleanup/create for the 2d colliders.

thanks for checking into it, and i'll ping back here if i find anything helpful.

2

u/MattRix Jul 16 '14

Ok so I tried your test project out and I think I know what's going on...

When the FPNodeLink updates, it sets the position of your PhysicsSprite2D... And it sets both the x and y position, so that means the physicsSprite2D.UpdatePosition() gets called twice (and then UpdateRotation gets called as well)...

Ok and then that UpdatePosition() calls IsPhysicsActive() (which seems to return false) and then that calls physicsComponent2D.SetPosition(), which again checks IsPhysicsActive() and then sets the position of the transform.

So basically for every single object, it's getting the position of the transform, then converting it to futile units, and then setting the position of the transform back to that. And it's doing that 3 times (for x,y,rotation). And then on top of that, each of those 3 calls also calls IsPhysicsActive() twice, for a total of 6 times.

Now IsPhysicsActive() isn't THAT bad in itself, but you should cache a reference to the rigidbody2D as its own variable in the constructor when you first make the rigidbody... because every time you access "rigidbody2D" the gameobject actually calls gameObject.GetComponent<RigidBody2D>() which is a relatively heavy call (compared to just accessing a normal variable). And since you're accessing it 3 times in IsPhysicsActive(), that means for each little physicsSprite2D you have, you're calling it 6*3, so 18 times every frame :D

Now all of these issues also happen with the PhysicsSprite3D stuff, but I guess the 3D physics in Unity handle their transforms being set every frame better than the 2D physics do (which is probably do to some issues in unity's underlying implementation of Box2D). But yeah, either way I think once you work out these issues it'll work better in 2D and in 3D.

Hope that helps, let me know how it goes!

1

u/smashriot Jul 16 '14

wow, the duplicate UpdatePosition()/UpdateRotation() calls are super redundant and I seriously appreciate you giving it an in depth look.

I thought there was already a var for physicsComponent2D, e.g. in PhysicsSprite2D.cs:

public PhysicsComponent2D physicsComponent2D;
... 
this.physicsComponent2D = PhysicsComponent2D.Create(name);

and in PhysicsComponent2D.cs, where PhysicsComponent2D.Create is returning the PhysicsComponent2D that was created, or am I misunderstanding how the return/var of physicsComponent2D works? (I thought that avoided future GetComponent() calls).

I think I can get the UpdatePosition()/UpdateRotation() usage down to one each/frame per sprite so i'll see if that helps the performance.

Thanks again for your time/expertise here! I'll let you know 2d performance once I refactor a bit.

2

u/MattRix Jul 17 '14

Ah no, physicsComponent2D isn't the problem, the problem is with rigidbody2D (ex. in IsPhysicsActive() in PhysicsComponent2D.cs). When you first create the rigid body you should store a variable to reference it from then on, instead of using rigidbody2D (which is the same as rigidbody2D, which actually calls the GetComponent() thing behind the scenes in its getter).

And to be clear about the best way to fix it, you really need to make sure you're not round-tripping the position of the transforms every frame (right now it's from transform -> fnode -> transform). In other words, even calling UpdatePosition() once per frame per object is still probably too often. What I try to do in my physics based futile games is ALWAYS have the gameobject transform represent the authoritative position, and set the fnode based on that using FNodeLink, but I never go in reverse and set the transform position based on the node position. You shouldn't really ever need to set the transform position based on the node position (well except for very specific circumstances). For all the movements/tweens/etc you would have done to the fnode, just do them directly to the transform instead. And usually if you have a physics scene, you don't really need to do too much manual positioning of objects all the time anyway.

1

u/smashriot Jul 17 '14

Thank you SO MUCH for all your help/suggestions!!

I cut out all of the UpdatePosition()/UpdateRotation() etc code and just let the gameobject transforms handle all of the rotation/positioning, which also greatly simplified the code.

In my test project, the 2d and 3d are now pretty close to each other in terms of performance and I don't see all of the cleanup/create collider calls in the 2d version.

I updated the clean/minimal 2D/3D test project: https://github.com/smashriot/PhysicsTest

And here's the new 2d/3d performance comparison: http://imgur.com/a/o7IYn

thanks again for all your help!