Tips and Tricks: Unity Lerp
Introduction
Using Unity’s Lerp methods can be quite confusing and error prone if done incorrectly. Despite this they are a vital part of creating a game. Allowing for smooth movement and transitions between things like positions, rotations, and even colors. You may want to use a lerp to fade the screen to black, rotate NPCs to look at the player, or transition a character sprite to green when they are poisoned.
To use any of the Unity Lerp methods you must provide a start value, an end value, and a rate of progression between the two. For methods you can call the generic Mathf.Lerp for float values, Vector3.Lerp for Vector3 values, Quaternion.Lerp for Quaternion values, and Color.Lerp for Color values. In this Tips and Tricks: Unity Lerp we are going to show you how to use each of these methods. As well as cover the do’s and don’ts to get them to work as expected.
Getting Started
What does Lerp mean
Lerp is defined as the linear interpolation between two values. These are usually two points in space or two Quaternion rotations. For example, applying a lerp to a Quaternion will smooth out the rotation so that it will move with linear progression towards the new position.
What does Interpolation Mean
According to Wikipedia “interpolation is a type of estimation, a method of constructing new data points within the range of a discrete set of known data points.” Basically, if you want to interpolate between the values of 0 and 5 at a rate of 1, an interpolation will return 0, 1, 2, 3, 4, 5.
Mathf.Lerp Example
We can prove this by printing the results of Mathf.Lerp. In Unity, Mathf.Lerp takes two float values and a percentage you wish to interpolate by. If we want our interpolation to increase at a rate of 1 between 0 and 5 then we want a 20% increase. We can place this code in the start method of a script attached to a GameObject.
void Start()
{
var lerpNumber = Mathf.Lerp(0f, 5f, .2f);
Debug.Log(lerpNumber);
}
Hit play in the editor and the console will output the value of 1, just as expected.
What is Unity Lerp
Beyond Mathf Lerp, Unity offers three other specific types of linear interpolation, Vector3.Lerp, Quaternion.Lerp, and Color.Lerp. These offer the same benefit as calling Mathf.Lerp but are specific to Vector3, Quaternion, and Color value types. Having specific types of Lerp methods allow you to pass in the specific type without having to do any conversions between the values and a float, which Mathf.Lerp takes as a parameter.
How to Use Lerp in Unity
All of the Lerp methods, Vector3.Lerp, Quaternion.Lerp, Color.Lerp, and Mathf.Lerp, take three parameters. The first is the start value, the second is the end value, and the third is the rate of progression in the form of a float value between 0 and 1. Again, you can think of the last value as a percent of change between the start and end values.
Why Lerp is not working in Unity
If lerp is called just once, then the value will only increment once by the rate specified. To solve this, Lerp is usually wrapped in Unity’s Update method, a while loop, or placed in a coroutine which contains a loop. Either way you are stuck trying to figure out how to update the values being passed to the lerp method. If you do not update them, you will always return the same value and it will appear that your Lerp has failed or stopped. For example, if we take the same code from above in our Start method and place it inside the Update method, we will always receive the same value of 1.
Why Lerp Never Reaches End Value
Furthermore, if we try to update our start value after each lerp by assigning the lerp output to our start value, the Lerp will never reach the end. The output will constantly increase at a percentage between the start value and end value.
float lerpNumber = 0f;
void Update()
{
lerpNumber = Mathf.Lerp(lerpNumber, 5f, .2f);
Debug.Log(lerpNumber);
}
This means that even if the number is 4.999, the next update will only increase the value to 4.9992, followed by 4.99936. This will continue towards infinity. Since we cannot update our end value because that is where we want to be, we are left with one value to change, the rate of change.
How to Calculate the Speed of Lerp
To properly calculate the rate at which the lerp should increase, we need to consider three things. The time that has passed since the lerp began, the distance the lerp needs to occur over, and the speed at which we want the transition to take place.
For time, we can easily set up a timer inside of our loop that increments the time passed. Unity’s Time.deltaTime is meant for exactly that purpose. Time.deltaTime returns the time since the previous frame update. Our timer will look like so.
float time = 0f;
time += Time.deltaTime;
Next, we need to evaluate the distance between our start and end values. Luckily, Unity offers a few helper methods to make our lives easier.
To get the distance for a Vector3.Lerp use the helper method Vector3.Distance and pass in the start and end values.
Vector3.Distance(startPosition, endPosition);
Next, to get the distance for a Quaternion.Lerp you will need to calculate the angle between the rotations. The helper method for this is Quaternion.Angle, which takes a start and end rotation as well.
Quaternion.Angle(startRotation, endRotation);
Finally, Color.Lerp. Unity does not offer a way to get the difference between colors but do not worry, cough, life, uh, finds a way…
When using Color or Color32, you can access the individual RGBA values as floats. This allows you to compare the values and sum the difference. This is a more manual process and you do need to get the absolute values by using Mathf.Abs but it works all the same. We have tried averaging the difference but that appeared to decrease the control we get from the rate or speed we provide.
float colorDifference = (Mathf.Abs(startColor.r - target.r)
+ Mathf.Abs(startColor.g - target.g)
+ Mathf.Abs(startColor.b - target.b)
+ Mathf.Abs(startColor.a - target.a));
Now, we can finally apply the last ingredient of our speed or rate calculation. This is a simple float value we can assign and tweak in the editor.
[SerializeField] private float speed;
For our final trick, we will combine these parts to get a rate of increase for our Lerps. To get the initial rate we will divide our time by our distance. This will give us a constant rate. We then can multiply this by our speed to control how fast we want the movement to be.
Vector3.Lerp(startPosition, target, (time/Vector3.Distance(startPosition, target))*speed)
How to Use Lerp in Unity Update Method and Why it is a Bad Idea
You can solve both the Lerp not moving and the Lerp never reaching the end issues by storing the start value separately when the lerp begins. You can then use the start value and end value to update the current value. Once the Lerp is over, you have to store the start value again to ensure it is ready for the next Lerp.
Here is an example of a Vector3.Lerp inside of the update method. Again, this works, but it is the more cumbersome way of doing it.
[SerializeField] private bool isLerpVector3;
[SerializeField] private Vector3 lerpPosition;
[SerializeField] private float speed;
private float time;
private Vector3 startPosition;
void Start()
{
startPosition = transform.position;
}
void Update()
{
if (isLerpVector3)
{
LerpVector3();
}
time += Time.deltaTime;
}
private void LerpVector3()
{
if(startPosition != lerpPosition)
{
transform.position = Vector3.Lerp(startPosition, lerpPosition, (time/Vector3.Distance(startPosition, lerpPosition))*speed);
}
else
{
startPosition = transform.position;
}
}
Executing your code this way is very confusing. Trying to use Unity’s Update lifecycle to update Vector3.Lerp, Quaternion.Lerp, Color.Lerp, or even Mathf.Lerp will most likely lead to issues. Even when you add the additional checks, fields for data storage, and helper methods it still ends up being slightly unmanageable. This could lead to hard-to-find bugs that will be even harder to refactor later on.
Another example of using the Update method for lerping. This time using Quaternion.Lerp. Do not do it this way unless there is some circumstance that prevents you from using the coroutine method discussed next.
[SerializeField] private bool isLerpQuaternion;
[SerializeField] private Vector3 lerpRotation;
[SerializeField] private float speed;
private float time;
private Quaternion startRotation;
private Quaternion endRotation;
void Start()
{
startRotation = transform.rotation;
endRotation = Quaternion.Euler(lerpRotation);
}
void Update()
{
if (isLerpQuaternion)
{
LerpQuaternion();
}
time += Time.deltaTime;
}
private void LerpQuaternion()
{
if(startRotation != endRotation)
{
transform.rotation = Quaternion.Lerp(startRotation, endRotation, time/ Quaternion.Angle(startRotation, endRotation) * speed);
}
else
{
startRotation = transform.rotation;
}
}
The recommended way to execute a Lerp function is inside of a coroutine. This way you can neatly wrap the loop inside of a method without having to use external values or fields. Coroutines run independent of the frame rate, which Update runs in. This means, if done correctly, the coroutine is a more performant way of processing your Lerps.
Here is an example of the same Vector3.Lerp but wrapped inside a coroutine. Notice there are less fields to keep track of. Even though the coroutine is started in the Update function, it immediately sets itself to false and the coroutine runs on its own. We also no longer have to reset our start position in a scenario where we will lerp multiple times.
[SerializeField] private bool isLerpVector3;
[SerializeField] private Vector3 lerpPosition;
[SerializeField] private float speed;
void Update()
{
if (isLerpVector3)
{
StartCoroutine(LerpPositionCoroutine(gameObject, lerpPosition, speed));
isLerpVector3 = false;
}
}
IEnumerator LerpPositionCoroutine(Vector3 target, float speed)
{
Vector3 startPosition = transform.position;
float time = 0f;
while(transform.position != target)
{
transform.position = Vector3.Lerp(startPosition, target, (time/Vector3.Distance(startPosition, target))*speed);
time += Time.deltaTime;
yield return null;
}
}
Summing up How to Lerp in Unity
There is a lot to take in here. Let us sum it up a bit.
- Perform your Lerps inside of a loop
- Do not update the start position during the loop
- Update the rate of increase by calculating time/distance multiplied by speed.
- Put your Lerp loops inside of Coroutines.
Unity Lerp Examples
We can make this code reusable by allowing it to take a GameObject, end value, and a speed. You could then place them all inside of a Lerp helper class and call them from any script they are needed in. Which allows you to stop rewriting code, saving time and energy.
Vector3 Lerp Example
IEnumerator Vector3LerpCoroutine(GameObject obj, Vector3 target, float speed)
{
Vector3 startPosition = obj.transform.position;
float time = 0f;
while(obj.transform.position != target)
{
obj.transform.position = Vector3.Lerp(startPosition, target, (time/Vector3.Distance(startPosition, target))*speed);
time += Time.deltaTime;
yield return null;
}
}
Quaternion Lerp Example
IEnumerator QuaternionLerpCoroutine(GameObject obj, Quaternion target, float speed)
{
Quaternion startRotation = obj.transform.rotation;
float time = 0f;
while(obj.transform.rotation != target)
{
obj.transform.rotation = Quaternion.Lerp(startRotation, target, (time/Quaternion.Angle(startRotation, target))*speed);
time += Time.deltaTime;
yield return null;
}
}
Material Color Lerp Example
IEnumerator ColorLerpCoroutine(GameObject obj, Color target, float speed)
{
Material mat = obj.GetComponent<Renderer>().material;
Color startColor = mat.color;
float time = 0f;
float colorDifference = (Mathf.Abs(startColor.r - target.r)
+ Mathf.Abs(startColor.g - target.g)
+ Mathf.Abs(startColor.b - target.b)
+ Mathf.Abs(startColor.a - target.a));
while(mat.color != target)
{
mat.color = Color.Lerp(startColor, target, (time / colorDifference * speed));
time += Time.deltaTime;
yield return null;
}
}
And now you are ready to use Unity’s Lerp methods to control the movement of objects in the games you create in Unity. Thank you for stopping by. Stick around and check out more of our tutorials or posts like our piece on Tips and Tricks: Unity Transparent Material. Also, leave a comment telling us what you liked or did not like about the tutorial. Was it easy to follow along? What do you want to learn next? As always check out some of our published apps below.