How does Cinemachine's Body "Hard Lock To Target" interpolation work under the hood?

I’ve been trying to understand how Cinemachine’s Body → “Hard Lock To Target” works whenever the Follow target is a rigidbody with interpolation set to “Interpolate”

I have a custom camera controller script which essentially just moves the camera to match the rigidbody’s current position. This obviously causes jittering because the rigidbody is moved inside FixedUpdate.

I can do things like lerping the position, but this is never perfect, often still having at least a little jittering and also the fact that the camera movement is now delayed.

So my question is: how does Cinemachine implement the interpolation so perfectly? It has zero jitter and the positioning isn’t delayed either.
I would like to achieve the same result with my own script but I am currently stuck. And I mean yes, Cinemachine does it for me but I would like to still learn!

What a great attitude! I salute you and applaud this attitude.

Fortunately for you, all of Cinemachine’s source code can be inspected!

Starting from where your Assets/ and ProjectSettings/ and Packages/ folders are located, go digging for something like:

Library/PackageCache/com.unity.cinemachine@2.7.9

Obviously version might be different.

The secret is that you have to do nothing special. If your RB is respecting the rules and Interpolation is working properly, other scripts (such as your camera controller) can just pretend that the RB is being updated in Update() - which is effectively what happens when interpolation is enabled. So you can track it in LateUpdate.

1 Like

Well I made this simplified test script. Am I missing something?

The result looks like this: https://i.imgur.com/7JGvplQ.mp4
Note that I’ve increased Time.fixedDeltaTime to make it more obvious.

But yeah, the camera is not following the rigidbody’s interpolated position, which is what I want, and which is what Cinemachine can do out of the box. Here it’s just getting the physics position, as expected.

using System.Collections;
using System.Collections.Generic;
using Cinemachine;
using UnityEngine;

public class SmoothRBCamera : MonoBehaviour
{
    public CinemachineVirtualCamera vcam;
    public Rigidbody playerRb;
    public float movementSpeed = 3f;

    [Space(16)]
    public Vector2 movementInput;
    public Vector3 currentPosition;

    private void Start()
    {
        Time.fixedDeltaTime = 0.1f;
    }

    private void Update()
    {
        movementInput = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")).normalized;
    }

    private void LateUpdate()
    {
        vcam.transform.position = playerRb.position;
    }

    private void FixedUpdate()
    {
        Vector3 input = playerRb.transform.forward * movementInput.y + playerRb.transform.right * movementInput.x;
        currentPosition += input * movementSpeed * Time.fixedDeltaTime;
        playerRb.MovePosition(currentPosition);
    }
}

Ahh okay, so my error was that I was using playerRb.position when it should be playerRb.transform.position, lol.

Now it appears to be interpolating like expected.

1 Like