This post is useful for those who are interested in a custom solution for motion-interpolation in Unity. If you are new to the topic of fixed timestep loops and motion-interpolation, here is an introduction to that:
https://gafferongames.com/post/fix_your_timestep/And here is the complementary thread that shows how to make fixed timestep logic more responsive and potentially even more performant:
https://forum.unity.com/threads/sta...motion-interpolation-global-solution.1547513/And this here documents how FixedUpdate() is timed in Unity:
https://docs.unity3d.com/Manual/TimeFrameManagement.htmlThe goal is to achieve stutter-free visuals for
deterministic game logic that runs in FixedUpdate(). This
TransformInterpolator script does just that:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// How to use TransformInterpolator properly:
/// 0. Make sure the gameobject executes its mechanics (transform-manipulations) in FixedUpdate().
/// 1. Make sure VSYNC is enabled.
/// 2. Set the execution order for this script BEFORE all the other scripts that execute mechanics.
/// 3. Attach this component to every gameobject that you want to interpolate (including the camera).
/// </summary>
public class TransformInterpolator : MonoBehaviour
{
private struct TransformData
{
public Vector3 position;
public Vector3 scale;
public Quaternion rotation;
}
//Init prevTransformData to interpolate from the correct state in the first frame the interpolation becomes active. This can occur when the object is spawned/instantiated.
void OnEnable()
{
prevTransformData.position = transform.localPosition;
prevTransformData.rotation = transform.localRotation;
prevTransformData.scale = transform.localScale;
isTransformInterpolated = false;
}
void FixedUpdate()
{
if (isTransformInterpolated) //reset transform to its supposed current state just once after each Update/Drawing
{
transform.localPosition = transformData.position;
transform.localRotation = transformData.rotation;
transform.localScale = transformData.scale;
isTransformInterpolated = false;
}
//cache current transform state (becomes previous by the next transform-manipulation in FixedUpdate() of another component)
prevTransformData.position = transform.localPosition;
prevTransformData.rotation = transform.localRotation;
prevTransformData.scale = transform.localScale;
}
void LateUpdate() //interpolate in Update() or LateUpdate()
{
if (!isTransformInterpolated) //cache the updated transform in transformData so that it can be restored in FixedUpdate() after drawing
{
transformData.position = transform.localPosition;
transformData.rotation = transform.localRotation;
transformData.scale = transform.localScale;
isTransformInterpolated = true; //it's ok to set it here since the anticipation matches the execution that follows
}
float interpolationAlpha = (Time.time - Time.fixedTime) / Time.fixedDeltaTime; //(Time.time - Time.fixedTime) is the "unprocessed" time according to documentation
transform.localPosition = Vector3.Lerp(prevTransformData.position, transformData.position, interpolationAlpha); //interpolate transform
transform.localRotation = Quaternion.Slerp(prevTransformData.rotation, transformData.rotation, interpolationAlpha);
transform.localScale = Vector3.Lerp(prevTransformData.scale, transformData.scale, interpolationAlpha);
}
private TransformData transformData;
private TransformData prevTransformData;
private bool isTransformInterpolated;
}
The alternative to custom motion-interpolation is
Rigidbody.interpolation, that is natively provided by Unity. Here are the pros and cons of custom motion-interpolation so that you can make your decision.
Custom motion-interpolation pros:- You can turn off the TransformInterpolator for offscreen objects to achieve significantly better performance than doing so by using Rigidbody.
- Transform is in sync with its state (not so otherwise: changes to Transform and Rigidbody.position are not immediate when moved by RigidBody.MovePosition()).
Custom motion-interpolation cons:- Less performant than Rigidbody.interpolation in general
Performance tips:- Don't interpolate Transform.localScale if it remains constant, as most objects don't grow in size. You might gain about 10% performance just by doing that if you have many objects on the screen. Engine calls can add up and get expensive.
- Turn off interpolation for offscreen objects.
- If the processing budget is too tight, it can still be Ok to just interpolate the camera and the main character/s. Running the TransformInterpolator for few objects won't be the bottleneck.