Search Unity

Resolved Recenter to target heading when button is pressed

Discussion in 'Cinemachine' started by Anshie, Sep 21, 2020.

  1. Anshie

    Anshie

    Joined:
    Dec 30, 2017
    Posts:
    6
    Hello!

    I started to use Cinemachine and I want to know if there's a way to make the camera recenter to target when, for example, the spacebar is pressed. What I mean is make the same thing the option "Recenter to target heading" does, but when a key is pressed.

    What I thought is to make a call to the method that's called when "Recenter to target heading" is enabled, so I've been searching in the Cinemachine scripts and what I got so far is to, indeed recenter the camera but only when I keep pressed the button, when I release the spacebar the camera movement stops in place.

    I just added a condition inside AxisState.cs that checks if the spacebar is pushed:
    Code (CSharp):
    1. public void DoRecentering(ref AxisState axis, float deltaTime, float recenterTarget)
    2.             {
    3.                 if (Input.GetButton("RecenterCamera")) {
    4.                     if (!m_enabled && deltaTime >= 0)
    5.                         return;
    6.  
    7.                     recenterTarget = axis.ClampValue(recenterTarget);
    8.                     if (deltaTime < 0)
    9.                     {
    10.                         CancelRecentering();
    11.                         if (m_enabled)
    12.                             axis.Value = recenterTarget;
    13.                         return;
    14.                     }
    15.  
    16.                     float v = axis.ClampValue(axis.Value);
    17.                     float delta = recenterTarget - v;
    18.                     if (delta == 0)
    19.                         return;
    20.  
    21.                     if (CinemachineCore.CurrentTime < (mLastAxisInputTime + m_WaitTime))
    22.                         return;
    23.  
    24.                     // Determine the direction
    25.                     float r = axis.m_MaxValue - axis.m_MinValue;
    26.                     if (axis.m_Wrap && Mathf.Abs(delta) > r * 0.5f)
    27.                         v += Mathf.Sign(recenterTarget - v) * r;
    28.  
    29.                     // Damp our way there
    30.                     if (m_RecenteringTime < 0.001f)
    31.                         v = recenterTarget;
    32.                     else
    33.                         v = Mathf.SmoothDamp(
    34.                             v, recenterTarget, ref mRecenteringVelocity,
    35.                             m_RecenteringTime, 9999, deltaTime);
    36.                     axis.Value = axis.ClampValue(v);
    37.                 }
    38.             }
    I know it maybe not be the correct place to put that condition. I'm pretty new to Unity, just used to make avatars on vrchat and I tried to make a Visual Novel while ago, so I'm not very experienced with how code works. Sorry for my english too, I'm not a native speaker.

    Thanks in advance!
     
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    You should not be modifying AxisState.cs - that's part of Cinemachine and you will lose your ability to upgrade easily if you start modifying it. Also AxisState is used in several places, probably the spacebar checking is not wanted in those other places.

    Instead, add a custom script to your vcam that sets the m_Enabled member of the recentering component inside your vcam, according to whether or not the spacebar is pressed.
     
    Anshie likes this.
  3. Anshie

    Anshie

    Joined:
    Dec 30, 2017
    Posts:
    6
    Thanks for your quick answer!

    Yup, at first I thought that I should create a new script and manage that variable there, but I couldn't get access to that variable (I don't really know how). How can I set that variable? I have a GameObject with the FreeLook camera on it, but I have no clue on how to get it on code. Getting the component of that GameObject gives me this error:
    upload_2020-9-21_22-13-33.png

    This is what I tried to do:
    Code (CSharp):
    1. GameObject PlayerCameraPC = GameObject.Find("PlayerCameraPC");
    2.         AxisState axis = PlayerCameraPC.GetComponentInParent<AxisState>();
    3.         axis.m_Recentering.m_enabled = true;

    Thanks in advance, and sorry for the newbie question, I'm just starting on this field!
     
  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    I'm not sure what Cinemachine component you're using. I'll assume for now that it's a CinemachineFreeLook. In that case, you would create a custom script, add it to that FreeLook. Then the code would be something like this:
    Code (CSharp):
    1. CinemachineFreeLook m_FreeLook;
    2.  
    3. void Start()
    4. {
    5.     m_FreeLook = GetComponent<CinemachineFreeLook>();
    6. }
    7.  
    8. void Update()
    9. {
    10.     m_FreeLook.m_RecenterToTargetHeading.m_enabled = myCondition;
    11. }
    12.  
    Note that once the condition is triggered (e.g. spacebar is presssed) you would probably want a little logic in there to hold recentering enabled until the recentering time has elapsed, and maybe cancel it if the user moves the mouse.
     
    Anshie likes this.
  5. Anshie

    Anshie

    Joined:
    Dec 30, 2017
    Posts:
    6
    Hello again!

    This morning I was trying out your code and now everything is working! I finally understood how the GetComponent works and I could recenter everything perfectly (the X and Y, because I noted that the RecenterToTargetHeading was just recentering the X axis).

    With a bit of work I added too that conditions, and everything is working fine. Maybe is not as smooth as it should, but for me is a great start.

    Thanks a lot for your help!!
     
    Gregoryl likes this.
  6. Meguchi

    Meguchi

    Joined:
    Nov 22, 2016
    Posts:
    4
    Heya, just wondering if you might be able to share your final code on this? I have been trying to get the FreeLook camera to re-centre behind the player on button press as well and just cant get it to work! Would really appreciate it if you could share your work!
     
  7. Mootook

    Mootook

    Joined:
    Oct 7, 2018
    Posts:
    2
    Hey Meguchi, I have a (eh, it works) solution implemented...though I'm looking for something a little better.
    Using new Unity's new input manager to fire UnityEvents on some button press to recenter the camera:

    Code (CSharp):
    1. public void OnCenterCamera(InputAction.CallbackContext context)
    2.     {
    3.          _camera.m_RecenterToTargetHeading.m_enabled = true;
    4.         _camera.m_YAxisRecentering.m_enabled = true;
    5.         // Disable the camera centering after it completes
    6.         // so it doesn't stay locked
    7.         // Multiply the completion timeout for some padding
    8.         int completionTimeout = (int) _camera.m_RecenterToTargetHeading.m_RecenteringTime * 1000; // 0.25 *1000
    9.            Task.Delay(completionTimeout * 2).ContinueWith(t => {
    10.             _camera.m_RecenterToTargetHeading.m_enabled = false;
    11.             _camera.m_YAxisRecentering.m_enabled = false;
    12.         });
    13.     }
    Main issue is completionTimeout (m_RecenteringTime) is more of a max speed from what I've gathered rather than the actual time it takes to smoothly transition the camera'a position to its "centered" state.
     
  8. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    Instead of waiting a fixed time, you could poll it and turn it off when the axis value is close enough to center
     
  9. Mootook

    Mootook

    Joined:
    Oct 7, 2018
    Posts:
    2
    Could you elaborate?
    I tried creating my own recentering functionality by caching the starting m_XAxis, and m_YAxis values, then lerping back to them when the player presses a button, but because those aren't consistent, as the objective my camera is to snap directly behind my player, that idea didn't go very far. I'm not sure I see which values always correspond to whether or not a FreeLook camera's centered to the target heading.
     
  10. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    The Y axis recentered value is 0.5. The X axis value would depend on your binding mode. If it's LockToTarget (or one of the variants), then "behind the player" would be 0. If it's WorldSpace, then "behind the player" would be the difference in Y rotation between player forward and world forward. If it's SimpleFollow then the desired recenter value would be the Y rotation difference between camera forward and player forward - note that this will change every frame, as the camera moves. You're recentered when that difference is sufficiently small.
     
    firaui and Mootook like this.