- Home /
How to move a kinematic rigidbody smoothly?
I'm about to write my own physics instead of using the build-in system because of this. Quick responses are highly appreciated.
EXAMPLE
Copy the script below and attach it to a plain GameObject in an empty scene.
WHAT YOU SEE
Object 1: Moving by setting transform.position from Update.
Object 2: Moving by calling rigidbody.MovePosition from FixedUpdate (isKinematic+Extrapolate).
Object 3: Moving by calling rigidbody.MovePosition from FixedUpdate (isKinematic).
Object 1: Moves smoothly.
Object 2: Moves not so smoothly.
Object 3: Moves like my grandmother.
QUESTION
How to move a kinematic rigidbody smoothly? Is it possible at all in Unity? And if not, why?
using UnityEngine;
public class JitterExample : MonoBehaviour
{
GameObject object1, object2, object3, object4;
const int MOVE_SPAN = 5;
const float SPEED = 2f;
void Start()
{
object1 = GameObject.CreatePrimitive( PrimitiveType.Cube );
object1.name = "Object1";
object2 = GameObject.CreatePrimitive( PrimitiveType.Cube );
object2.name = "Object2";
object2.AddComponent<Rigidbody>();
object2.rigidbody.isKinematic = true;
object2.rigidbody.interpolation = RigidbodyInterpolation.Extrapolate;
object3 = GameObject.CreatePrimitive( PrimitiveType.Cube );
object3.name = "Object3";
object3.AddComponent<Rigidbody>();
object3.rigidbody.isKinematic = true;
Camera.main.orthographic = true;
Camera.main.orthographicSize = 5;
Camera.main.transform.position = new Vector3( MOVE_SPAN * SPEED * 0.5f, 0, -5 );
RenderSettings.ambientLight = Color.black;
}
void Update()
{
object1.transform.position = new Vector3( Mathf.PingPong( Time.time, SPEED ) * MOVE_SPAN, 2, 0 );
}
void FixedUpdate()
{
object2.rigidbody.MovePosition( new Vector3( Mathf.PingPong( Time.time, SPEED ) * MOVE_SPAN, 0, 0 ) );
object3.rigidbody.MovePosition( new Vector3( Mathf.PingPong( Time.time, SPEED ) * MOVE_SPAN, -2, 0 ) );
}
void OnGUI()
{
GUILayout.Label( "WHAT YOU SEE" );
GUILayout.Label( "\tObject 1: Moving by setting transform.position from Update." );
GUILayout.Label( "\tObject 2: Moving by calling rigidbody.MovePosition from FixedUpdate (isKinematic+Extrapolate)." );
GUILayout.Label( "\tObject 3: Moving by calling rigidbody.MovePosition from FixedUpdate (isKinematic)." );
GUILayout.Space( 20 );
GUILayout.Label( "QUESTION" );
GUILayout.Label( "How to move a kinematic rigidbody smoothly?" );
}
}
Have you tried fiddling around with the iTween plugin?
Did you find a way to have your smooth movement? I'm having the same issue.
Answer by Bunny83 · Sep 29, 2012 at 01:49 AM
The simple answer is: Don't use FixedUpdate. FixedUpdate was never ment to provide a smooth call rate. It just "fixes" framerate fluctuations to have a statistically constant call-rate. (another use for my FixedUpdate simulator :D ).
FixedUpdate is important for non-linear calculations (like acceleration) because such calculations can't be scaled with deltaTime. All linear stuff (which is the most stuff) should be done in Update and "fixed" with Time.deltaTime.
If you really need to move it in FixedUpdate (for interactions with normal rigidbodies) you might want to increase the FixedUpdate rate (lower the fix-timestep).
What I see about fixedUpdate is it is about having constant physics step, so the physics (and thus the collisions) are precise. It avoid having, in case of low framerate, some case of "jumpimg over" object, avoiding collision with walls or stuff.
I could use continuous collision, but if I apply it on all my gameobjects, it will cost a lot of CPU.
It is why we need to use FixedUpdate for our movement, so that the physics will give us constant and stable collision computing.
But FixedUpdate runs not in smooth steps in contrast to your visual framerate. The movement will always "look" more choppy when done in FixedUpdate. Yes, FixedUpdate helps a bit to avoid "collision misses" but don't ele$$anonymous$$ate them. When an object moves "too fast" it will miss the collision anyways.
So how should I move rigidbody so its animation looks smooth and it respect the collisions as a kinematic body? What would the script looks like?
Answer by Selmar · Jun 20, 2015 at 06:10 PM
I don't know if it existed in 2012, but there is also the .Interpolate option, which interpolates between the current and the previous position of the rigidbody.
However, for me, that did not help either. I'm currently running into similar problems using rigidbodies, where I slow down the time of an object. I execute an update function that calculates acceleration/velocity/position whenever the local time exceeds the physics update interval. I apply the position using MovePosition. However, the rigidbody does not interpolate properly between frames, because it thinks the object pauses between frames.
Since the rigidbody moves the gameobject (and this messes with interpolation), I have detached the physics object from the visual object, so I can do the interpolation myself. I don't know whether this is good or bad design, but I do feel like this is not entirely the way Unity was meant to be used. It works just fine, though.
The interpolation source code as was requested:
protected virtual void LateUpdate()
{
if (!_positionComponent) return;
switch (PositioningMethod)
{
case EPositioningMethod.ExtrapolateWithVelocity:
// if no move component, don't do interpolation
if (!_moveComponent)
goto case EPositioningMethod.UpdateWithPositionComponentTransform;
_extrapolateWithVelocity();
break;
case EPositioningMethod.InterpolateWithPreviousPosition:
if (!_moveComponent)
goto case EPositioningMethod.UpdateWithPositionComponentTransform;
_interpolateWithPrevPosition();
break;
case EPositioningMethod.UpdateWithPositionComponentTransform:
transform.position = _positionComponent.transform.position + _offset;
break;
case EPositioningMethod.UserDefined:
break;
case EPositioningMethod.InitWithPositionComponentTransform:
// just set position once
if (!_initPosition)
{
transform.position = _positionComponent.Position + _offset;
_initPosition = true;
}
break;
default:
// default case is no smoothing (but still setting position)
goto case EPositioningMethod.UpdateWithPositionComponentTransform;
}
}
void _interpolateWithPrevPosition()
{
float timeToInterpolate = View.GetUnscaledFixedDeltaTime();
if (_moveComponent.Position + _offset != _lastFixedPosition)
{
_lastCurrentPosition = transform.position;
_lastFixedPosition = _moveComponent.Position + _offset;
_t = 0;
//transform.position = _lastFixedPosition;
}
else
{
if (_t < timeToInterpolate)
_t += View.GetDeltaTime();
if (_t > timeToInterpolate)
_t = timeToInterpolate;
transform.position = Vector3.Lerp(_lastCurrentPosition, _lastFixedPosition, _t / timeToInterpolate);
}
}
void _extrapolateWithVelocity()
{
if (_moveComponent.Position + _offset != _lastFixedPosition)
{
_lastFixedPosition = _moveComponent.Position + _offset;
transform.position = _lastFixedPosition;
}
else
transform.position += _moveComponent.Velocity * View.GetDeltaTime();
}
I am browsing the internet for last two weeks and finally you post does look like it would help my problem. I need accurate platformer physics and i have done it with raycasting and translating, only problem is i need to take movement to FixedUpdate() so i can have consistent gameplay. Of course, then visual update becomes a problem. Your interpolation idea sounds great. If its no trouble, could you please post some more details about implementation ?
I updated the answer with the source code used for interpolation. This should help!
I know this is an old comment, but I just wanted to say how helpful this post is, and how thankful I am that you shared it. It definitely deserves more upvotes :)
Answer by cecarlsen · Sep 30, 2012 at 11:11 AM
My question was answered on the forum. Sorry I forgot to link the two posts:
I ended up solving the problem by increasing the fixed update rate, thereby taking a considerable performance hit.
ANd that's why I don't want to have a performance hit that I cannot increase the fixed update rate.
Still searching.
I checked the topic you linked, the logical solution to have smooth movement AND a good physics with kinematic rigidbody would be to write
void Update() { rigidbody.$$anonymous$$ovePosition(transform.position + speed * Time.deltaTime); }
I tried it, but it still jittered a bit.