Search Unity

Snap or animate Cinemachine free look camera to look at a specific point

Discussion in 'Cinemachine' started by MattiaPez, Jan 8, 2020.

  1. MattiaPez

    MattiaPez

    Joined:
    Jan 8, 2020
    Posts:
    3
    Hey, I'm using a cinemachine free look camera for my character. In the game I can select an item, look around and then press a button to refocus the camera to the selected item. Now, this seemed really easy when I initially looked at it but the results are not great. My approach is to find the rotation the camera should have and use the euler degrees to set the m_xAxis field of the free look camera. When I do this via code it results in a weirdly snapping movement (can be seen in the video).
    At the end the camera does not like this and I have no clue on how to get y axis value (that needs to be between 0 and 1). Our camera uses a screen x offset to 0.2 that complicates the calculations.

    My question is: is there a built in method I'm missing? Is there a simpler way (maybe with camera transitions) to do it? Any help would be appreciated, thanks.

    I'm using CM version 2.3.4.

    Attached a package to reproduce the problem in a sandbox scene and a video to quickly see the issue.
     

    Attached Files:

  2. marc_tanenbaum

    marc_tanenbaum

    Unity Technologies

    Joined:
    Oct 22, 2014
    Posts:
    637
    It's possible I'm not understanding your problem correctly, but might it work to simply change the LookAt target?

    Code (CSharp):
    1. private void Update()
    2.     {
    3.         if (Input.GetKeyDown(KeyCode.Space))
    4.         {
    5.             m_freeLookCamera.m_LookAt = m_target;
    6.         }
    7.         if (Input.GetKeyUp(KeyCode.Space))
    8.         {
    9.             m_freeLookCamera.m_LookAt = transform;
    10.         }
    11.     }
     
  3. MattiaPez

    MattiaPez

    Joined:
    Jan 8, 2020
    Posts:
    3
    @marc_tanenbaum Firstly thanks for your answer. I did try that solution but it just makes the camera rotate to look at the target. What I would like to achieve is to have the camera always behind the player as shown in the video and have the camera moving around the player (and rotating to keep the look at target of course) until the position and rotation makes the camera look at the target (the center of the screen should point to the target's position). Basically I want the same behaviour as it is driven by an axis, but as input I have a direction vector, or a rotation.
     
  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    Hey @MattiaPez thanks for packaging that up, you saved a lot of back-and-forth.

    What you're trying to do is a little tricky, but possible. You can use the delta between current camera forward and desired camera forward to deduce the direction in which you need to move the axes, and move them a bit in that direction each frame until you get there.

    Here is your Example.cs script, modified to get the effect that (I think) you want:
    Code (CSharp):
    1. using System.Collections;
    2. using Cinemachine;
    3. using Cinemachine.Utility;
    4. using UnityEngine;
    5.  
    6. public class Example : MonoBehaviour
    7. {
    8.     public Transform m_target;
    9.     public CinemachineFreeLook m_freeLookCamera;
    10.     private bool m_rotating;
    11.  
    12.     // Update is called once per frame
    13.     private void Update()
    14.     {
    15.         if (Input.GetKeyDown(KeyCode.Space))
    16.         {
    17.             StartCoroutine(LookAtTarget(GetFreeLookYRange()));
    18.         }
    19.     }
    20.  
    21.     /// <summary>
    22.     /// Call this to cancel the rotation, e.g. if the user rotates the FreeLook
    23.     /// </summary>
    24.     public void CancelRotation()
    25.     {
    26.         m_rotating = false;
    27.     }
    28.  
    29.     /// <summary>
    30.     /// This is supposed to snap (or animate) the camera axis to look at the target
    31.     /// </summary>
    32.     private IEnumerator LookAtTarget(float yRange)
    33.     {
    34.         // This constant controls the speed of the lerp.  1 is fastest.
    35.         const float lerpAmount = 0.3f;
    36.         m_rotating = true;
    37.         while (m_rotating && m_target != null && m_freeLookCamera != null)
    38.         {
    39.             // How far away from center is the target?
    40.             var state = m_freeLookCamera.State;
    41.             var dir = m_target.position - state.CorrectedPosition;
    42.             var rot = state.CorrectedOrientation.GetCameraRotationToTarget(dir, state.ReferenceUp);
    43.  
    44.             // Record the current settings
    45.             var current = new Vector2(m_freeLookCamera.m_YAxis.Value, m_freeLookCamera.m_XAxis.Value);
    46.  
    47.             // First do the yaw
    48.             m_freeLookCamera.m_XAxis.Value = (current.y + rot.y * lerpAmount) % 360;
    49.  
    50.             // Then do the pitch
    51.             m_freeLookCamera.m_YAxis.Value = Mathf.Clamp01(current.x + lerpAmount * rot.x / yRange);
    52.  
    53.             // Are we there yet?
    54.             if (Mathf.Abs(m_freeLookCamera.m_YAxis.Value - current.x) < 0.01f
    55.                 && Mathf.Abs(m_freeLookCamera.m_XAxis.Value - current.y) < 0.01f)
    56.             {
    57.                 m_rotating = false; // yes, close enough
    58.             }
    59.             yield return null;
    60.         }
    61.     }
    62.  
    63.     /// <summary>
    64.     /// Deduces the Y axis angle range from the freeLook orbits
    65.     /// </summary>
    66.     private float GetFreeLookYRange()
    67.     {
    68.         if (m_freeLookCamera == null)
    69.             return 1;
    70.         var orbit = m_freeLookCamera.m_Orbits[2];
    71.         var d0 = new Vector3(0, orbit.m_Height, orbit.m_Radius);
    72.         orbit = m_freeLookCamera.m_Orbits[0];
    73.         var d1 = new Vector3(0, orbit.m_Height, orbit.m_Radius);
    74.         return Vector3.Angle(d0, d1);
    75.     }
    76. }
     
    Last edited: Jan 9, 2020
    haldanemcfall likes this.
  5. MattiaPez

    MattiaPez

    Joined:
    Jan 8, 2020
    Posts:
    3
    @Gregoryl That worked quite good for us, thank you very much for your answer! Would be great to have something like this built in, I guess this is a common feature. Amazing work anyway!
     
    Gregoryl likes this.