- Home /
Moving a kinematic object a fixed distance over a set time with physics in mind
What would be best performance-wise for the given situation?
Situation: I want to move a kinematic object a fixed distance over a set time without negatively affecting physics or unintentionally causing performance bottlenecks.
For context, the object I would like to move has a BoxCollider(3D), a Rigidbody, is kinematic, and is not a trigger.
I designed my own interpolation system that enables me to move objects smoothly by simply changing the Transform directly on each Update() call. This involves calculating the next position to assign based on how much distance is left and so on. I didn't think this would be a problem until I started profiling recently. I am learning now that is bad to change the Transform directly if it has a Rigidbody and the scene itself has many instances of this.
This led me to consider two (recommended) options that involve using FixedUpdate instead:
Method 1: Change all references of 'Transform.position' to 'Rigidbody.position' when both accessing and setting the world position of the object to move. I use my own interpolation to calculate the correct target position on each FixedUpdate() call based on the distance, duration, and so on.
Method 2: Use 'Rigidbody.MovePosition'. There is some type of built-in interpolation that makes movement smooth between frames? You then call this until the Rigidbody reaches a desired destination. You are presumably at the mercy of Unity's built-in interpolation and cannot utilize any of your own?
To my knowledge, 'Rigidbody.MovePosition' is to be used if you know how fast you want the object to go and if you know what direction the object should move in, but not if there is a known destination and the object has to reach it there in a specific amount of time. I have chosen Method 1 as my project requires objects to move a fixed distance and over a set time.
Is Method 1 the correct way to move a kinematic object with my own interpolation while still respecting physics?
My own interpolation in this case is just saying I want to use an easing function like "Ease In Sine" to move an object from point A to point B in so many seconds.
By 'respecting physics', what I mean is,
If another Rigidbody that is non-kinematic were to fall on top of the moving object (which is moved using Method 1), does it move with the object below once it lands like you would expect it to?
Imagine a floating platform that moves linearly on the x-axis. If a human were to appear out of nowhere directly above this platform and fall to the platform below (either while the platform is stationary or in the process of moving), can the human now basically ride the platform?
If you were to move the object by changing Transform.position instead of Rigidbody.position, the human would not be able to ride the platform. The platform would simply move out from underneath of the human and the human would fall to whatever was below. The workaround I had to come up with was to define an ADDITIONAL collider that was a trigger and sat on the top surface of the platform. Whenever something would collide with it, I would basically update the position of the object that was to ride it based on wherever the platform was on any given Update() call. I'm sure it's not difficult to see why this is bad and where I start to get confused.
Is this an issue related to changing the position of a Rigidbody outside of the physics engine? Or is this a simple limitation of the physics engine itself and something you have to compensate for? Does a non-kinematic object have to have a certain mass relative to the object that moves in order for both to move in unison?
Answer by Edy · Jul 01, 2021 at 09:37 AM
The correct way to resolve this case is calling Rigidbody.MovePosition() from FixedUpdate.
You know the current position (Rigidbody.position), the distance to move and the time required. Some simple math calculations using Time.deltaTime within FixedUpdate can give you the next position to feed MovePosition() with. Internally MovePosition computes the velocity out of the difference with the previous position (Rigidbody.velocity), allowing the kinematic body to cause the expected effects in other non-kinematic bodies.
Visual interpolation is then provided by the Rigidbody.interpolate setting. Intermediate visual positions (transform.position) are then calculated automatically in each visual frame. You shouldn't try to implement your own interpolation, as it would be easy to mess up the physics simulation.
Modifying transform.position or rigidbody.position won't resolve the case either. Modifying any of them means "teleporting" the rigidbody to the new position, so the physics effects on other bodies won't be correct.
Thank you for this clarification. I am not looking to reinvent whatever interpolation Unity does, but I do need to be able to use an easing function. I wasn't sure if you could do that without directly modifying Rigidbody.position.
Here is an example of what I am using to achieve easing currently by changing Rigidbody.position:
//The destination
public Vector3 Target;
//How far the object has travelled up to this point
float journey;
protected Vector3 GetNewPosition(Vector3 origin, float percent)
{
var percent = Mathf.Clamp01(journey / Convert.ToSingle(Duration.TotalSeconds));
var value = new float[3];
for (var i = 0; i < 3; i++)
{
var o0
= i == 0
? origin.x
: i == 1
? origin.y
: origin.z;
var t0
= i == 0
? Target.x
: i == 1
? Target.y
: Target.z;
float EaseInSine(float start, float end, float v)
{
end -= start;
return -end * Mathf.Cos(v * (Mathf.PI * 0.5f)) + end + start;
}
value[i] = EaseInSine(o0, t0, percent);
value[i] = float.IsNaN(value[i]) ? 0 : value[i];
}
return new Vector3(value[0], value[1], value[2]);
}
Given a start position (origin) and an end position (target), I would calculate a new position based on the "Ease In Sine" function. I'm not sure if Unity's interpolation is similar to this or something else entirely, but I'm assu$$anonymous$$g Unity uses linear interpolation by default and doesn't support anything else? This is where I get confused. Can I still use an easing function like this using Rigidbody.MovePosition()? Is the interpolation Unity uses different from the type of interpolation (easing) I describe above? I hate to ask, but if you could provide an example with code, it might make more sense.
That code is perfectly fine. Simply pass the resulting position to Rigidbody.MovePosition() within FixedUpdate(). That will move the kinematic rigidbody following the calculated path while causing the expected physics effects in the non-kinematic bodies.
Unity's interpolation is a different thing affecting the visual position only (not the physics). Physics simulation (FixedUpdate) runs at a fixed rate (50 Hz by default), while visual updates happen at a different rate defined by the graphics hardware (i.e. 60, 144, 200 fps etc). Without interpolation the physics objects look jerky when moving. Unity's interpolation (Rigidbody.interpolate) takes the latest two physics positions and linearly calculates the intermediate steps to fill the visual frames between those positions. The result is the physics objects visually moving smoothly regardless the visual frame rate.
Answer by tyruji · Jun 30, 2021 at 06:34 PM
Don't use the Update message for moving rigidbodies, use FixedUpdate. If you want the interpolation to be done for you use rigidbody.MovePosition, and in your case in which you are doing it on your own, just change rigidbody.position. Also, you're right that changing transform.position creates overhead when a rigidbody is attached.
Your answer
Follow this Question
Related Questions
Simple Movement Game: Physics vs Manual Collision Detection? 2 Answers
stop vehicle from moving through walls 1 Answer
Kinematics: Trying to get an object to land at a known point 1 Answer
Clamping rigidbody's Z position causing issues in the editor, when transform hits zero on Z axis 0 Answers
Rigid body child Player movement 0 Answers