Rigidbodies in Unity do not like their position set.
“Do not use the Transform component to set the position or rotation of a Dynamic Rigidbody 2D. The simulation repositions a Dynamic Rigidbody 2D according to its velocity; you can change this directly via forces applied to it by scripts or indirectly via collisions and gravity.”
- https://docs.unity3d.com/Manual/class-Rigidbody2D.html
The documentation outlines we change a Dynamic Rigidbody's position by using AddForce or setting the velocity directly. A Kinematic rigidbody is designed to use the MovePosition function.
This leaves out a common necessity for dynamic rigidbodies. How do we set the (exact) position of a dynamic rigidbody object for initialization? If we want to programmatically reuse (in the case of object pooling) or initialize a physics scene, we’re going to have to set a rigidbody’s exact position.
Six Experiments
If you are looking for the best way I’ve discovered to set position, scroll to the bottom. If you are interested in my process for determining this “best practice” stick around.
Here is my setup:
(White Object) - A dynamic rigidbody, circle collider, gravity scale 0, linear drag 0. This is the physics object that needs to be set to a Target Position.
(Orange Object) - The Target Position. A transform that gets set to a random position within a unit circle.
(Purple Object) - A dynamic rigidbody, with a Spring Joint attached to the White Object. This object will always apply some kind of force to the white object, which will be important when we start moving it.
(Red Objects) - A dynamic rigidbody, circle collider, gravity scale 0. These float around the scene to test collisions when moving an object from position A to B. More on that later.
(Green Circle) - A Point Effector with negative force. This acts as a gravity well effecting the dynamic bodies in the scene.
Here we have a relatively dynamic physics scene. The White Object will try to set its position to the Orange Object. It has many forces that can act upon it - the purple object attached via a spring, the red bodies floating around in the scene, and the effector applying a negative force on them.
This environment will put a “strain” on our attempts to set the White Object’s position directly. So lets walk through our options.
Option 1) Add Force / set velocity
Before breaking open the physics textbook we have to understand that, unless our dynamic rididbody has NO attached physics objects, is existing in a physics free scene, with no physics material or drag effecting it, we can not reasonably calculate how to get from point A to point B through one call to Add Force or setting velocity.
This is the point at which most people (including myself) end up googling “Rigidbody2D Set Position” and the abyss presents itself.
Option 2) Set Position
Rigidbody2D exposes a .position property. The documentation doesn’t really say much about it. It’s the position. BUT! You can set it. We set the position of the body, and then measure its distance from the target on the next physics tick (the position does not update automatically).
Looks great! Our body is at the exact position we want it to be.
Okay, what if we attach a body to it via a spring, or turn on an effector in our scene?
It looks like we are accruing an error proportional to some kind of force. Unless we can make sure that our rigidbody doesn’t get effected by some force between position A and B then we can use this method. Not a very reasonable expectation, especially for physics heavy scenes.
ISSUES
Accrues error via connected bodies or effectors
NOTES
Is NOT effected by the drag of the physics body
Is NOT effected by colliders between position A and B
Option 3) Dynamic MovePosition
The documentation doesn’t outright tell us we can ONLY use MovePosition for Kinematic bodies, it just says
So, what happens?
We accrue a TINY error in an ideal case and a large error if we hit an object between point A and point B (underlined in red), but curiously enough, it is possible to hit our target position exactly.
If we enable our attached spring we see an even more exaggerated error.
ISSUES
Accrues error via connected bodies or effectors
Hits object between point A and point B if colliders are enabled
You can avoid this by deactivating a collider before calling MovePosition
NOTES
Is NOT effected by the drag of the physics body
Option 4) Kinematic Move Position
Reading the documentation for Body Type: Kinematic tells us:
“A Kinematic Rigidbody 2D does still move via its velocity, but the velocity is not affected by forces or gravity..”
This seems like it will allow us to isolate our physics object from forces, so lets test. MovePosition is still an incremental movement though, so we need to disable our colliders before moving the object.
One more thing, changing the body type on the fly accrues a performance cost
“Changing the Body Type of a Rigidbody 2D can be a tricky process. When a Body Type changes, various mass-related internal properties are recalculated immediately, and all existing contacts for the Collider 2Ds attached to the Rigidbody 2D need to be re-evaluated during the GameObject’s next FixedUpdate. Depending on how many contacts and Collider 2Ds are attached to the body, changing the Body Type can cause variations in performance.”
Results:
This is more consistent than our other attempts. In fact, I thought this was the solution while I was logging the float values to 6 decimal places. Only until I implemented a distance == 0f conditional to change text from green to yellow did I notice this was accruing any error at all. Yikes!
ISSUES
Accrues a TINY error
Accrues a performance hit switching between Dynamic and Kinematic body types.
Hits object between point A and point B if colliders are enabled
NOTES:
Is not effected by attached bodies or scene effectors
Option 5) Kinematic Position
So, MovePosition is probably doing something under the hood that’s incremental, uses some kind of force or velocity solver to figure out how to get an object from point A to point B, but as we can see, there exists some very tiny errors with this.
What if we set the position directly while the object is Kinematic?
I slammed my spacebar for like 3 minutes to make sure this was correct - Scientific Game Developer Magazine
OKAY SO!!! This works! In a scene with all these objects blocking trajectory, springs attached to the bodies, effectors pulling it this way and that. This sets the position EXACTLY.
ISSUES
Requires us to Change Body Type and accrue a performance hit
I thought this was it, the end of the road. Until necrosoft’s Shane wondered if it was possible to utilize the dynamic rigidbody’s position and rotation constraints while setting the position. . .
Option 6) Freeze Constraints
❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄ ❄
It didn’t occur to me that this would do anything. I thought if the position was constrained, the physics system would just bounce the position set like a check.
Results:
Beautiful . . . not only does this method accrue no error, have no inhibitions with its attached bodies or scene effectors, it does not require us to change body type. We can stay dynamic, cache our existing restraints if necessary, freeze everything, and set the position!
Conclusion
If you scrolled down, the best method i’ve discovered is Option 6) Freeze.
I wrote up my findings here (and created this substack) because I was receiving so much mixed information on forums and elsewhere about the best way to do this. I think a lot of projects don’t require precise enough physics to where investigating to this depth is necessary, but the solution i’ve come to is pretty simple and provides an EXACT result, so hopefully this post saves some people from future physics headaches.
Special thanks again to Shane from Necrosoft and Dom Harris for hearing me out with my initial issues.
If I’ve missed anything, or you have a better solution, or you can point me to the exact part of the documentation I overstepped explaining the best way to do this . . . . let me know!
You can download the test project I used to determine this best practice from my github:
https://github.com/xom0b/Rigidbody2DSetPosition
You can follow me on twitter here, and my studio, wonkware . Our upcoming game will feature these breakthroughs in rigidbody usage. Peace and love.