Search code examples
c#unity-game-enginecameracoroutinelerp

How can I get my camera to momentarily pause between different positions when using lerp in Unity3D?


I have an array of positions that I want my camera to move/lerp between. There are two buttons (button A and button B) that trigger the camera to move position. If the user presses button A, the camera will lerp to the previous position in the array. If the user presses button B, the camera will lerp to the next position in the array. However, before moving to a new position, I want the camera to lerp to an intermediate position, pause there for a couple of seconds, and then move. Here is the pseudocode for what I have at the moment:

 void Update() 
     {
         if (buttonPress == a) {
             positionToMoveTo = positions[currentPosition--];
         } 
         if (buttonpress == b) {
             positionToMoveTo = positions[currentPosition++];
         }
     }

 void LateUpdate() 
    {
         camera.lerp(intermediatePosition);
         StartCoroutine(pause());
    } 

 IEnumerator pause() 
    {
         yield return new WaitForSeconds(3f);
         camera.lerp(positionToMoveTo);
    }

This doesn't work though because I get strange jittering when switching camera positions and my intermediate position doesn't always occur. I think my problem has something to do with execution order but I can't figure it out. Any help would be great :)


Solution

  • You start a new Coroutine every frame since LateUpdate runs every frame after all Update calls are finished!

    You could avoid this by a slightly different approach:

    private bool isIntermediate;
    private bool moveCamera;
    
    private void LateUpdate ()
    {
        if(!moveCamera) return;
    
        if(isIntermediate)
        {
            camera.lerp(intermediatePosition);
        } 
        else 
        {
            camera.lerp(positionToMoveTo);
        }     
    }
    
    private IEnumerator MoveCamera() 
    {
        moveCamera = true;
        isIntermediate=true;
        yield return new WaitForSeconds(3f);
        isIntermediate=false;
    
        // Wait until the camera reaches the target
        while(camera.transform.position == PositionToMoveTo){
            yield return null;
        }
    
        // Stop moving
        moveCamera = false;
    
        // just to be sure your camera has exact the correct position in the end
        camera.transform.position = PositionToMoveTo;
    }
    

    Alternatively you could do all the movement in the Coroutine without LateUpdate (but honestly I'm not sure if the Coroutines are done before or after Update)

    private IEnumerator MoveCamera() 
    {
        float timer = 3f;
        while(timer>0)
        {
           timer -= Time.deltaTime;
           camera.lerp(intermediatePosition);
           yield return null;
        }
    
        // Wait until the camera reaches the target
        while(camera.transform.position == PositionToMoveTo){
            camera.lerp(PositionToMoveTo);
            yield return null;
        }
    
        // just to be sure your camera has exact the correct position in the end
        camera.transform.position = PositionToMoveTo;
    }
    

    This second one would be cleaner bjt as said I don't know if it is a requirement for you to have it run in LateUpdate


    Note: the == operator of Vector3 has a precision of 0.00001. If you need a better or weaker precision you have to change to

    if(Vector3.Distance(camera.transform.position, PositionToMoveTo) <= YOUR_DESIRED_THRESHOLD)
    

    Now all you have to do is to call your Coroutine Everytime you want to change the camera position.

    void Update() 
     {
         if (buttonPress == a) 
         {
             // Make sure the Coroutine only is running once
             StopCoroutine(MoveCamera);
             positionToMoveTo = positions[currentPosition--];
             StartCoroutine (MoveCamera);
         } 
         if (buttonpress == b) 
         {
             // Make sure the Coroutine only is running once
             StopCoroutine (MoveCamera);
             positionToMoveTo = positions[currentPosition++];
             StartCoroutine (MoveCamera);
         }
     }