Issue using Vector3.Lerp to zoom Cinemachine camera - Lerp is being triggered twice

I’m using Vector3.Lerp to set up my Cinemachine camera to create a zoom effect when the player collects a powerup. When the player collects a powerup it calls ZoomCameraOn(), the camera zooms out, and after a certain amount of time (when the powerup timer expires), the camera zooms back to its normal position.

This way, if the user collects a powerup while the camera is zooming in to its normal position, the camera will seamlessly start zooming out from its current position.

If I only collect a power-up once to trigger the camera movement, it works fine. It’s only when you interrupt the zooming process by collecting another power-up that the problems start.

If you collect a powerup while the camera is zooming back to its normal position, the camera zooms out fine, but when it’s time for the camera to zoom back in after the timer expires, it plays the zoom effect twice.

  • The first time it zooms in, the camera’s starting point is from where it was when the zoom out was triggered after a powerup was collected.

  • The second time, the camera zooms in as it should. It’s starting point is from the end point from where the camera finishes zooming out.

Here’s the code:

public CinemachineVirtualCamera cinemachineVirtualCamera;
public CinemachineTransposer cinemachineTransposer;

public Vector3 startPosition_ZoomOn;
public Vector3 startPosition_ZoomOff;

public bool cameraZoom;
public float timer;

void Awake()
{
    cinemachineTransposer = cinemachineVirtualCamera.GetCinemachineComponent<CinemachineTransposer>();
}

void Update()
{
    if (cameraZoom && timer > 0)
    {
        timer -= 2f * Time.deltaTime;
    }

    if (timer <= 0)
    {
        timer = 10f;
        ZoomCameraOff();
    }
}

public void ZoomCameraOn()
{
    startPosition_ZoomOn = cinemachineTransposer.m_FollowOffset;
    Vector3 endPosition = new Vector3(0, 4, -9);

    StartCoroutine(ZoomCameraOn_LerpPosition(endPosition, 3));

    cameraZoom = true;
    timer = 10f;
}

IEnumerator ZoomCameraOn_LerpPosition(Vector3 targetPosition, float duration)
{
    float time = 0;

    while (time < duration)
    {
        if (cameraZoom == true)
        {
            cinemachineTransposer.m_FollowOffset = Vector3.Lerp(startPosition_ZoomOn, targetPosition, time / duration);
            time += Time.deltaTime;
        }

        yield return null;
    }

    cinemachineTransposer.m_FollowOffset = targetPosition;
}

public void ZoomCameraOff()
{
    startPosition_ZoomOff = new Vector3(0, 4, -9);
    Vector3 endPosition = new Vector3(0, 3, -7);

    StartCoroutine(ZoomCameraOff_LerpPosition(endPosition, 2));

    cameraZoom = false;
}

IEnumerator ZoomCameraOff_LerpPosition(Vector3 targetPosition, float duration)
{
    float time = 0;

    while (time < duration)
    {
        if (cameraZoom == false)
        {
            cinemachineTransposer.m_FollowOffset = Vector3.Lerp(startPosition_ZoomOff, targetPosition, time / duration);
            time += Time.deltaTime;
        }

        yield return null;
    }

    cinemachineTransposer.m_FollowOffset = targetPosition;
}

I don’t know what’s causing this. I tried stopping the zoom in coroutine when the zoom out is triggered, thinking it needed to finish the first zoom-in before doing another, but that didn’t work.

I’d be grateful for any help as I’ve hit a brick wall trying to solve this problem.

You’re on the right path when you’re stopping coroutines, but before looking at any code changes, it’s probably easier to use Cinemachine in the way it’s meant to be used - don’t modify it’s values at runtime, switch to a different active VCam instead, and use the Custom Blends on the CinemachineBrain to make the transition look right.

Then your code would simply be something like this:

public CinemachineVirtualCamera zoomOutCam; // This should be disabled, and have a higher priority than your usual camera.
public float zoomOutDuration;

private Coroutine zoomOutRoutine;

public void ZoomCameraOn()
{
    if (zoomOutRoutine != null)
        StopCoroutine(zoomOutRoutine);

    zoomOutRoutine = StartCoroutine(ZoomOut());
    IEnumerator ZoomOut()
    {
        zoomOutCam.enabled = true;
        yield return new WaitForSeconds(zoomOutDuration);
        zoomOutCam.enabled = false;
    }
}
2 Likes

Cheers for the reply and for the code, appreciate it.

I never thought about using Custom Blends, but I’ll definitely look this up and hopefully this’ll be what i’m looking for.

Thanks again!