There are many causes for objects to stutter when being moved in Unity. This guide explains the different options for smooth movement and explains what lerping and deltatime are as well as how to use them! (Very important and useful to learn when developing Unity games)
Possible causes for movement jitter
Not using a rigidbody:
- Script not taking frame rate into consideration when moving an object without a rigidbody.
An object being moved 1 pixel per frame would jump and change speed as the frame rate changes!
- Position not being lerped or interpolated.
Lerping will smoothly blend from position a to position b, read the lerping section below for more info!
When using a rigidbody:
- Interpolation not set when using a rigidbody.
Select your rigidbody and set interpolation, explained more below in the rigidbody movement section.
- Rigidbody is marked as kinematic.
Kinematic rigidbodies will act as if you’re just setting .position of the rigidbody.
- Rigidbody doesn’t have a collider, or using a non-convexed mesh collider.
A rigidbody without a valid collider can have unexpected physics issues.
- Script interacting with rigidbody isn’t ran in the FixedUpdate() loop
Make sure any per-update code interacting with the rigidbody is inside the FixedUpdate function so it’s ran per physics update!
- Physics timestep is set too high in the time manager
The physics timestep is the amount of seconds between each physics update, if this value is set too high it can result in jittery movement
Less common issues which I’ve seen in the past:
- Scale of the object you’re trying to move is either massively too big or very tiny
Aim to keep your your objects based around a 1, 1, 1 scale
- Multiple pieces of code trying to move the same object at once
Try adding some debug logging to make sure the movement code isn’t being called multiple times – if not disable it and make sure no others scripts are moving the object!
- Setting the position of an object which already has a rigidbody with gravity
Directly setting the .position of a moving object per frame will create a lot of stuttering.
Adapting movement to the framerate with deltatime
Moving objects based on frame rate is very important for creating smooth movement. Otherwise when the frame rate of the game changes, the speed of the objects will also change!
Unity has a variable named deltaTime (Time.deltaTime) which gives the time in seconds since the last Update() call as well as fixedDeltaTime (Time.fixedDeltaTime) which is the constant time since the last FixedUpdate() (Which is a consistant value changed in the time manager or directly changed via script, but it’s handy to have) (Tip: Using Time.deltaTime inside a FixedUpdate() will give you the fixedDeltaTime value)
Note: Using only deltaTime alone may still give stutter at lower framerates or at higher movement speeds. You will want to use deltaTime movement in combination with either lerping or a interpolated rigidbody! (Both explained in their sections below!)
But wait.. how does the deltaTime help me with slower movement?
Lets say you have a ball moving across the screen at 1 pixel per frame. If the frame rate of the game is 100 then the ball will have moved 100 pixels in 1 second. If the game is running at 10 frames per second (you can never be sure what frame rate you game will be running at on all systems!) then the ball will move 10 pixels in 1 second, meaning it’s moving 10 times slower!
We can use the time since last frame (deltaTime) to instead move the ball by (deltaTime * speed) pixels per frame which will make the ball always travel the same distance over the same amount of time, moving at a consistent speed. This is because as the frame rate is lowered the time since last frame actually increases and visa-versa for lower frame rates.
How does lerping work?
Lerping is where you interpolate a value from start to finish, this basically means to fill in the gap between start and finish gradually. An example is if you’re wanting to lerp from A to Z instead of jumping directly you could jump to everything in between to make the change feel smoother. (A, B, C, D, E… etc)
Unity Lerp Functions
Unity has a several lerp functions for various variable types, but this guide is explaining smooth movement so I’ll be focusing on Vector3.Lerp but referencing Mathf.Lerp for explaining some basics.
Vector3.Lerp takes 3 parameters: Vector3 start position, vector3 end position and a float lerp value. The lerp value is a float between 0 and 1 and determines which ‘in between point’ from start to end should be returned. For example using the Mathf.Lerp function. (which is used for lerping float values) If we called Mathf.Lerp(0f, 10f, 0.5f); it would return 5f as that’s 50% of the way between the start of 0 and end of 10.
One option for using Vector3.Lerp is to completely move from a starting position to an end position over time. You would create a variable to count over time, increasing it by (Time.deltaTime * countingSpeed) then use it as float lerp value to smoothly generate a vector3 position which moves from start to finish. (You would then set the .position of the transform you wanted to move each time the lerp is updated)
Another way to use Vector3.Lerp is to lerp from the previous position to the wanted position and use deltatime as the lerp value:
transform.position = Vector3.Lerp(transform.position, wantedPosition, Time.deltaTime * lerpSpeed);
Rigidbodies are the names of physics objects in Unity. Attach the rigidbody component and you are given several options for gravity, interpolation and collision. (Note: Make sure your rigidbody has a collider roughly matching the mass of your object or you may experience unexpected weird physics)
The interpolation setting on rigidbodies controls movement smoothing, with this disabled movement will appear to stutter, especially on a fast moving rigidbody with a higher timestep value!
Interpolate or extrapolate?
Setting the rigidbody to interpolate will make the rigidbody act similarly to a normal lerp, it’ll lag behind the wanted position more if the rigidbody is moving fast. Extrapolation predicts movement based on the current velocity of the rigidbody which allows it to keep up when the rigidbody is moving fast. However extrapolation can result in a mild ‘rubber banding’ effect where if the rigidbody hits a collider at high speed it may appear to clip inside or through it for one frame until the rigidbody realizes that the collider is there in the next frame.
Moving a rigidbody
Once your rigidbody is setup with interpolation, kinematic is unticked and it has a collider attached (make sure if you’re using a mesh collider it is marked as convex!) you can now move it via a script.
If you just want to directly move it into position with your own Vector3 value then make sure you’re updating the position inside of the FixedUpdate() method which is called every fixed update. (physics update frequency is set by the Fixed Timestep value inside the Time Manager)
To set the position of a rigidbody whilst also using its interpolation settings, call:
Alternatively if you need to force set the position of your rigidbody call:
myRigidbody.position = wantedPosition;
Time.deltaTime = Time since last frame (aka last Update() call)
Time.fixedDeltaTime = Contant time since last fixed physics update (aka last FixedUpdate() call)
Vector3.Lerp(startPosition, endPosition, lerpValue) = Lerp value is a value between 0 and 1 which acts as a percentage returning a vector which is ‘lerpValue’ percent between startPosition and endPosition
myRigidbody.MovePosition(wantedPosition) = Tell a rigidbody to move into position (Using interpolation to smoothly move to the position based on interpolation settings and only if isKinematic is false)