Search Unity

Change FreeLook binding mode seamlessly (from SimpleFollow to WorldSpace)

Discussion in 'Cinemachine' started by BastienDigixart, Jul 24, 2018.

  1. BastienDigixart

    BastienDigixart

    Joined:
    Apr 4, 2017
    Posts:
    13
    Hello !

    I have a FreeLook camera with a SimpleFollowWithWorldUp heading and when the player wants to control the camera position, I would like to set it to the World Space. Then, after x second without any camera input, I want to put back the SimpleFollowWithWorldUp binding.

    Currently, I cant find a way to do this seamlessly (I tried to report the m_XAxis.Value but it doesnt seem to work at all).

    I want this because the camera controls in SimpleFollowWithWorldUp looks like the player is fighting against the default camera behaviour.

    Thank you !
     
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    I can suggest having 2 FreeLooks, with the different modes, and blending between them. Make sure you check the InheritPosition checkbox on them, and that they have exactly the same Follow target.

    upload_2018-7-24_9-37-29.png
     
  3. BastienDigixart

    BastienDigixart

    Joined:
    Apr 4, 2017
    Posts:
    13
    Hey, thanks for your answer. However, given our workflow we cant really use this solution unfortunatly (ld have set a lot of different cameras). Isnt there any way to change the binding mode seamlessly ?
     
  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    Changing the binding mode changes the way the axis values are interpreted. You would have to write a piece of code that converts the X axis value from one space to the other at the same time as when you switch out the binding mode.

    In world space binding, the X axis value is the amount in degrees that the camera offset is rotated away from negative world Z.

    In SimpleFollow, the binding is in camera space, which is why the X axis value is always 0. X axis represents camera forward's deviation from camera forward.

    So just put 0 in the X axis value when you switch to SimpleFollow. When you switch to world, calculate where the camera is relative to the target, and put that angle in X axis value.
     
  5. BastienDigixart

    BastienDigixart

    Joined:
    Apr 4, 2017
    Posts:
    13
    This is exactly what I'm doing but it doesnt seem to work.
    When I change the binding from the World to SimpleFollow it reset the camera to the world Z. Its like if it use the 0 in X axis in world space and then switch to SimpleFollow. But I do at the same time. I tried it in a coroutine after a yield return new WaitForEndOfFrame, or a yield return null but its still the same.
    For the SimpleFollow to the World, the camera seem to take a rotation not so far but definitly a pop there.
     
  6. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    Try at the same time freeLook.PreviousStateIsValid = false; to reset the state info
     
  7. BastienDigixart

    BastienDigixart

    Joined:
    Apr 4, 2017
    Posts:
    13
    Its the exact same :(
     
  8. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    Can you put together a simple project with only this inside it?
    Send it to me and I will take a look.
    Tell me also what version CM and what version Unity.
     
  9. BastienDigixart

    BastienDigixart

    Joined:
    Apr 4, 2017
    Posts:
    13
    I dont know if I can put a project together but I can make you a video. We use the CM 2.2.0 with Unity 2017.4.5f1.
     
  10. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    Please post your script that does the switcheroo
     
  11. BastienDigixart

    BastienDigixart

    Joined:
    Apr 4, 2017
    Posts:
    13
    Here you are ! Its a test script I put together to test the binding switch.
    For the SimpleFollowToWorld I'll check the maths first.

    upload_2018-7-24_18-26-34.png

    The Brain is set to LateUpdate.
    No other bits of scripts modify the x axis.
     
    ReglStudios likes this.
  12. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    Thanks for the snippet. I was able to reproduce the problem. The solution is to add one more line, because the FreeLook is caching stuff from the previous frame.

    Here is my version of the mode-changing script.
    Code (CSharp):
    1. using UnityEngine;
    2. using Cinemachine;
    3.  
    4. public class BastienSwitcheroo : MonoBehaviour
    5. {
    6.     CinemachineFreeLook freeLook;
    7.  
    8.     void Start()
    9.     {
    10.         freeLook = GetComponent<CinemachineFreeLook>();
    11.     }
    12.    
    13.     void Update()
    14.     {
    15.         if (freeLook != null)
    16.         {
    17.             if (Input.GetKeyDown(KeyCode.Space))
    18.             {
    19.                 if (freeLook.m_BindingMode == CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp)
    20.                     SwitchToWorldBinding();
    21.                 else
    22.                     SwitchToSimpleFollow();
    23.             }
    24.         }
    25.     }
    26.  
    27.     void SwitchToWorldBinding()
    28.     {
    29.         Vector3 offset = freeLook.State.RawPosition - freeLook.Follow.position;
    30.         offset.y = 0; // project onto plane
    31.         float value = Vector3.SignedAngle(Vector3.back, offset, Vector3.up);
    32.         freeLook.m_BindingMode = CinemachineTransposer.BindingMode.WorldSpace;
    33.         freeLook.InternalUpdateCameraState(Vector3.up, -1);
    34.         freeLook.m_XAxis.Value = value;
    35.         freeLook.PreviousStateIsValid = false;
    36.     }
    37.  
    38.     void SwitchToSimpleFollow()
    39.     {
    40.         freeLook.m_BindingMode = CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp;
    41.         freeLook.InternalUpdateCameraState(Vector3.up, -1);
    42.         freeLook.m_XAxis.Value = 0;
    43.         freeLook.PreviousStateIsValid = false;
    44.     }
    45. }
     
    Skaster87 and BastienDigixart like this.
  13. BastienDigixart

    BastienDigixart

    Joined:
    Apr 4, 2017
    Posts:
    13
    It works perfectly !!!
    Thank you very much for this, you make us save a lot of time !

    Have a nice day !
     
  14. JescoInazuma

    JescoInazuma

    Joined:
    Dec 12, 2020
    Posts:
    10
    I am having a view issues with this, for some reason i still get weird artefacts sometimes, even when using similar code. I am switching from WorldBinding to LookToTarget and i get a flicker for one Frame, is there anything i did wrong or overlooked?

    Code (CSharp):
    1. private void ApplyBindingMode(CinemachineFreeLook camera) {
    2.             float value;
    3.             switch (m_BindingMode) {
    4.                 case CinemachineTransposer.BindingMode.WorldSpace :
    5.                     value = SwitchToWorldBinding(camera);
    6.                     break;
    7.                 case CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp :
    8.                     value = SwitchToSimpleFollow(camera);
    9.                     break;
    10.                 case CinemachineTransposer.BindingMode.LockToTarget :
    11.                     value = SwitchToLookToTarget(camera);
    12.                     break;
    13.                 default:
    14.                     Debug.LogError($"Switching the Camera to BindingMode '{m_BindingMode}' at runtime isn't implemented yet");
    15.                     return;
    16.             }
    17.             camera.InternalUpdateCameraState(Vector3.up, -1);
    18.             camera.m_XAxis.Value = value;
    19.             camera.PreviousStateIsValid = false;
    20.         }
    21.      
    22.         private float SwitchToWorldBinding(CinemachineFreeLook camera) {
    23.             Vector3 offset = (camera.State.RawPosition - camera.Follow.position).xOz();
    24.             float value = Vector3.SignedAngle(Vector3.back, offset, Vector3.up);
    25.             camera.m_BindingMode = CinemachineTransposer.BindingMode.WorldSpace;
    26.             return value;
    27.         }
    28.  
    29.         private float SwitchToSimpleFollow(CinemachineFreeLook camera) {
    30.             camera.m_BindingMode = CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp;
    31.             return 0;
    32.         }
    33.  
    34.         private float SwitchToLookToTarget(CinemachineFreeLook camera) {
    35.             Vector3 offset = (camera.State.RawPosition - camera.Follow.position).xOz();
    36.             float value = Vector3.SignedAngle(- camera.Follow.forward.xOz(), offset, Vector3.up);
    37.             camera.m_BindingMode = CinemachineTransposer.BindingMode.LockToTarget;
    38.             return value;
    39.         }
     
    Last edited: Aug 20, 2021
  15. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    @Jescolnazuma It might be script execution order (when do you call this? Before or after the target moves?) or, depending on how you've set up your FreeLook, there may be a difference between raw position and corrected position. Try using
    camera.State.CorrectedPosition
    instead of
    camera.State.RawPosition
    .
     
  16. FGRivera

    FGRivera

    Joined:
    Jun 3, 2013
    Posts:
    16
    Hi there! I recently came across this solution for my needs and it works almost perfectly for my project save for one issue: there is a tiny snap in rotation the frame after changing binding modes. I noticed that this hitch goes away when I remove horizontal and vertical damping from the Aim of the rigs. Any way I can conserve the smoothing through the transition to keep it smooth?
     
  17. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    The most reliable way is to use 2 FreeLooks, one with each Binding Mode, and activate/deactivate them as needed. The Brain will take care of providing a smooth blend. Changing binding modes on the fly is risky because it's not an intended use case.
     
  18. FGRivera

    FGRivera

    Joined:
    Jun 3, 2013
    Posts:
    16
    Bummer. It adds so much more overhead to have to manage two cameras for what almost works perfectly with this solution.
     
  19. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    It's not so bad. If the binding mode is the only difference, you could author only one FreeLook and have a script that duplicates it at runtime, changes the binding mode, and activates the copy when required.