Search Unity

  1. Check out the Unite LA keynote for updates on the Visual Effect Editor, the FPS Sample, ECS, Unity for Film and more! Watch it now!
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  4. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  5. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

Trouble Recentering The X Axis of the Free look Camera

Discussion in 'Cinemachine' started by bagle45, Jul 5, 2018.

  1. bagle45


    Jul 25, 2016
    We've been Integrating Cinemachine into our party game, and I'm really impressed with the feature set so far, especially the free-look camera.

    We've hit a bit of a roadblock however, with how to recenter the camera on the X Axis. I've tried setting
    m_RecenterToTargetHeading.m_enabled to true, as well as m_XAxis.DoRecentering(), without any result. By Contrast, the Y Axis recentering works as expected.

    Furthermore, I'm curious as to where the state of the X Axis is stored in the free-look camera. The "Value" of the X Axis is always 0, and setting it to anything other than 0 effects a small rotation before it returns to 0. Does the value you enter here correspond to a rotation of "Value "Degrees in the X axis?

    We've tried Cinemachine Versions 2.1.10 & 2.2 with Unity versions 2017.4 & 2018.1. We can reproduce this in a fresh 2018.1 project using the "Freelook character" Cinemachine example scene.

    Thank you.
  2. Gregoryl


    Unity Technologies

    Dec 22, 2016
    This is probably because you have the Binding Mode set to Simple Follow with World Up. This is a camera-space binding mode, meaning that the "reset" position is always in the direction that the camera is pointing - so it's always centered, which is why the axis Value is always 0. Try changing the binding mode to something else, like Target with World Up. Binding Mode defines the context in which the Axis Value is interpreted.
  3. bagle45


    Jul 25, 2016
    Hi Gregoryl, Thanks for the info, that explains quite a lot.

    I'm afraid I don't see a binding mode like this. I see a number of "lock to target" options, and a "world space" option, which makes the camera behave like the "Z-Targeting" mode form the Legend of Zelda games.

    I thought maybe I could effect what I was after by swapping between binding modes, performing the recenter, and then swapping back to Simple Follow, but the camera keeps snapping back to it's original rotation the moment I swap the binding mode back to Simple Follow, regardless of the values I assign to to the X axis before doing so.

    Thanks again for your help!
  4. Gregoryl


    Unity Technologies

    Dec 22, 2016
    You should be able to get what you want without swapping anything around.
    My problem: I don't really understand what you want. Can you describe the sorts of movements the player will be doing, and how you'd like the camera to respond?
  5. bagle45


    Jul 25, 2016
    The game is a third person shooter similar to splatoon, so the character will be running & jumping like most 3rd person shooters/platformers. The "Simple Follow with World Up" Camera behaves exactly how we want the camera to behave. I'm just looking for a way to handle two edge cases:
    1) recenter the camera behind the player (Preferably with a quick rotation around the player to get there) when a "Recenter" button is pressed.
    2) When transitioning from another camera to this one, have the free look camera rotation start with the same rotation as the camera it just transitioned from.
  6. Gregoryl


    Unity Technologies

    Dec 22, 2016
    Well, the concept of "centered" doesn't really exist for the SimpleFollow binding mode. However, it is possible to write a script to recenter it, according to the script's own definition of centered. Here is one that rotates the FreeLook to be behind the target. Just add it to the FreeLook.
    Code (CSharp):
    1. using UnityEngine;
    2. using Cinemachine;
    3. using Cinemachine.Utility;
    4. public class SimpleFollowRecenter : MonoBehaviour
    5. {
    6.     public bool recenter;
    7.     public float recenterTime = 0.5f;
    8.     CinemachineFreeLook vcam;
    9.     CinemachineOrbitalTransposer[] orbital = new CinemachineOrbitalTransposer[3];
    10.     CinemachineVirtualCamera[] rigs = new CinemachineVirtualCamera[3];
    12.     void Start()
    13.     {
    14.         vcam = GetComponent<CinemachineFreeLook>();
    15.         for (int i = 0; vcam != null && i < 3; ++i)
    16.         {
    17.             rigs[i] = vcam.GetRig(i);
    18.             orbital[i] = rigs[i].GetCinemachineComponent<CinemachineOrbitalTransposer>();
    19.         }
    20.     }
    22.     void Update()
    23.     {
    24.         Transform target = vcam != null ? vcam.Follow : null;
    25.         if (target == null)
    26.             return;
    28.         // Disable the transposers while recentering
    29.         for (int i = 0; i < 3; ++i)
    30.             orbital[i].enabled = !recenter;
    32.         if (recenter)
    33.         {
    34.             // How far away from centered are we?
    35.             Vector3 up = vcam.State.ReferenceUp;
    36.             Vector3 back = vcam.transform.position - target.position;
    37.             float angle = UnityVectorExtensions.SignedAngle(
    38.                 back.ProjectOntoPlane(up), -target.forward.ProjectOntoPlane(up), up);
    39.             if (Mathf.Abs(angle) < UnityVectorExtensions.Epsilon)
    40.                 recenter = false; // done!
    42.             // Do the recentering on all 3 rigs
    43.             angle = Damper.Damp(angle, recenterTime, Time.deltaTime);
    44.             for (int i = 0; recenter && i < 3; ++i)
    45.             {
    46.                 Vector3 pos = rigs[i].transform.position - target.position;
    47.                 pos = Quaternion.AngleAxis(angle, up) * pos;
    48.                 rigs[i].transform.position = pos + target.position;
    49.             }
    50.         }
    51.     }
    52. }
    For the second edge case, open the FreeLook's "Transitions" section in the inspector and put these settings:


    Let me know if these solutions work out for you.
    Last edited: Jul 7, 2018
    IanItor likes this.
  7. bagle45


    Jul 25, 2016
    Wow, that script is exactly what I needed, thank you so much Gregoryl! Really appreciate it!
    I made just a few small tweaks to it. It takes a quite a while for the damp function to reach epsilon, so I put a much larger number in there instead. It also causes a null reference exception in the free-look camera's inspector (line 163), but a simple null check fixed that:
    Quaternion orient = MiddleRig.GetReferenceOrientation (up); 

    Quaternion orient = Quaternion.identity;

    if (MiddleRig)
    orient = MiddleRig.GetReferenceOrientation (up);

    I tried using blend hints as you suggested, and they're quite nice. I was just having a little difficulty getting them to behave when using instantiated prefabs containing the virtual cameras. They seemed to just be ignored it most cases.
    Part way through testing things though, I realized that the blend hints weren't quite what I was looking for in any case. I wanted to have a bit more control over how the Free look framed what the previous camera what just looking at, and so, with the insight your wonderful code gave me, I came up with the following:
    Code (CSharp):
    1. void setFollowingCamForward (Vector3 forward, UnityAction onChangeActive = null)
    2. {
    3.         forward.Normalize ();
    5.         CinemachineFreeLook followCam = m_minion.FollowCam;
    7.         Vector3 targetPos = followCam.Follow.position;
    9.         Vector3 camForward = targetPos - followCam.transform.position;
    11.         // Set forward on all 3 rigs
    12.         for (int i = 0; i < 3; ++i)
    13.         {
    14.                 followCam.GetRig (i).GetCinemachineComponent<CinemachineOrbitalTransposer> ().enabled = false;
    16.                 Vector3 pos = followCam.GetRig (i).transform.position - targetPos;
    17.                 pos = Quaternion.AngleAxis(angle, Vector3.up) * pos;
    19.                 Vector3 newPos = targetPos - forward * followCam.m_Orbits[i].m_Radius +
    20.                         Vector3.up * followCam.m_Orbits[i].m_Height;
    22.                 followCam.GetRig (i).transform.position = newPos;
    23.         }
    25.         StartCoroutine (enableOrbitals (onChangeActive));
    26. }
    28. IEnumerator enableOrbitals (UnityAction onChangeActive = null)
    29. {
    30.         yield return null;
    32.         CinemachineFreeLook followCam = m_minion.FollowCam;
    34.         for (int i = 0; i < 3; ++i)
    35.         {
    36.                 followCam.GetRig (i).GetCinemachineComponent<CinemachineOrbitalTransposer> ().enabled = true;
    37.         }
    39.         yield return null;
    41.         if (onChangeActive != null)
    42.         {
    43.                 onChangeActive ();
    44.         }
    45. }
    Once again, thank you very much for your help Gregoryl!
    Last edited: Jul 9, 2018
    Gregoryl likes this.