Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Cinemachine Freelook ignores UI

Discussion in 'Cinemachine' started by piginhat, Aug 19, 2019.

  1. piginhat

    piginhat

    Joined:
    Feb 17, 2016
    Posts:
    83
    If I use the default setup whereby on mobile the CMFreelook is controlled by finger I find that it still accepts input if the finger movement is on a UI control.

    I have a joystick to control the players movement around the scene which works fine but as I move the joystick as the player moves the CMFreelook is moving too.
     
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    3,072
    Your UI control has to consume the event to prevent it from being passed along to the rest of the game.

    I don't understand your second sentence. Can you clarify please?
     
  3. piginhat

    piginhat

    Joined:
    Feb 17, 2016
    Posts:
    83
    Sure, I can use my finger to move the freelook camera and it works fine. I then take my finger off the screen and then tap and hole the virtual UI joystick than moves the player as I move the joystick, but at the same time as I move my finger whilst on the joystick the freelook camera continues to process the finger movement
     
  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    3,072
    Sounds like the same problem. Your virtual joystick needs to consume the UI event.
     
  5. piginhat

    piginhat

    Joined:
    Feb 17, 2016
    Posts:
    83
    I understand re checking if the touch is over a UI using EventSystem.current.IsPointerOverGameObject(touch.fingerId) and have used this when interacting with buttons successfully, but I think my issue is different as it is the CMFreelook I believe should be testing for this surely?
     
  6. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    3,072
    I don't see how the FreeLook can check for that. It just takes input from the Input system. You need to make sure that the Input system doesn't get events that are meant for your UI. Maybe I'm mistaken, but I think Event.Use is supposed to take care of that.
     
  7. piginhat

    piginhat

    Joined:
    Feb 17, 2016
    Posts:
    83
    I have modified the custom input method to deal with touches and this seems to have fixed the problem in part.

    Although no it ignores the touch if over UI when I am not over UI I have to tap to move the camera it ignores the smooth sliding of the finger?

    So to clarify, when I am using the default Input for freelook I also have another UI joystick that controls the player.

    I can use my finger to control the camera and I can use the joystick to move the player and when touching the joystick the input no longer passes through to the freelook

    Code (CSharp):
    1.  
    2. public float GetAxisCustom(string axisName) {
    3.    
    4.         // if joysticks paused
    5.         if (GameManager.instance.pauseJoysticks) {
    6.        
    7.             // we don't want any inputs from them
    8.             return 0;
    9.         }
    10.  
    11.         // if using joystick
    12.         if (useCustomAxis) {
    13.  
    14.             if(axisName == "Mouse X") { // this is set in Editor-Inspector-CineMachineFreelook-AxisControl->X Axis
    15.  
    16.                 // return our input rather than system Mouse X
    17.                 return  UltimateJoystick.GetHorizontalAxis( "CameraJoystick" );
    18.             }
    19.             else if (axisName == "Mouse Y") { // this is set in Editor-Inspector-CineMachineFreelook-AxisControl->Y Axis
    20.  
    21.                 // return our input rather than system Mouse Y
    22.                 return UltimateJoystick.GetVerticalAxis( "CameraJoystick" );
    23.             }
    24.         }
    25.  
    26.         if (Input.touchCount > 0) {
    27.  
    28.             Touch touch = Input.GetTouch(0);
    29.  
    30.             if (EventSystem.current.IsPointerOverGameObject(touch.fingerId)) {
    31.  
    32.                 return 0;
    33.             }
    34.             else {
    35.  
    36.                 return Input.GetAxis(axisName);
    37.             }
    38.         }
    39.  
    40.         return Input.GetAxis(axisName);
    41.     }
    42.  
     
    Last edited: Sep 7, 2019
  8. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    3,072
    I don't know exactly what this does:
    Code (CSharp):
    1. if (EventSystem.current.IsPointerOverGameObject(touch.fingerId))
    Did you test that it is true when the touch is over the joystick? Try with Debug.Log in there to prove that it's the right test. Maybe there's a method inside UltimateJoystick that can tell you when it's being touched.
     
  9. piginhat

    piginhat

    Joined:
    Feb 17, 2016
    Posts:
    83
    Yes it is detecting the touch.

    I moved the touch code into the Update method and introduced a flag like so:

    Code (CSharp):
    1.  
    2. if (Input.touchCount > 0) {
    3.  
    4.             Touch touch = Input.GetTouch(0);
    5.  
    6.             // if over a UI element
    7.             if (EventSystem.current.IsPointerOverGameObject(touch.fingerId)) {
    8.  
    9.                 // ignore input
    10.                 disableFreelook = true;
    11.             }
    12.             else {
    13.  
    14.                 disableFreelook = false;
    15.             }
    16.         }
    17.  
    and then in the custom axis code I check like so:

    Code (CSharp):
    1.  
    2.        if (disableFreelook) {
    3.  
    4.             return 0;
    5.        }
    6.      
    7.  
    8.        return Input.GetAxis(axisName);
    9.  
    which results in the same single touch issue as described above.

    If I comment out the if statement in the custom axis code it works smoothly with finger but again the original issue returns. So the touch is being detected but then seems to only action the input per touch once rather than continuing to use the touch updates?
     
  10. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    3,072
    I suspect that EventSystem.current is only valid in some of the calls. Is there a way to query the joystick without making use of EventSystem.current?
     
  11. piginhat

    piginhat

    Joined:
    Feb 17, 2016
    Posts:
    83
    I will ask the creator of the Joystick and let you know...
     
  12. sacb0y

    sacb0y

    Joined:
    May 9, 2016
    Posts:
    182
    I am also having this issue, where the freelook camera reads position even if I'm selecting ui.

    It seems to activate under any touch event.
     
  13. piginhat

    piginhat

    Joined:
    Feb 17, 2016
    Posts:
    83
    Unfortunately for me I took the easy way out and decided to use a joystick for both freelook and player movement and not give the user a choice to use finger for freelook....I just did not want to spend anymore time on it :-(
     
  14. sacb0y

    sacb0y

    Joined:
    May 9, 2016
    Posts:
    182
    That's a shame, touch camera controls is crucial to my game, is there any info i can provide to help @Gregoryl ?\

    The funny thing is this is not an issue in unity remote, only on the actual build on the android platform. So it's really hard to test if i've even fixed the problem...
     
  15. piginhat

    piginhat

    Joined:
    Feb 17, 2016
    Posts:
    83
    The dev of Ultimate Joystick tried to help with using EventSystem.current.use() and touchinfo.use() in various methods but not resolved the issue.
     
  16. octavioroar

    octavioroar

    Joined:
    Aug 29, 2018
    Posts:
    1
    I've been having the same problem for the last few days and still can't find a solution. The problem I'm having is with the joystick too, there seems to be no problem with UI buttons, though.
     
  17. ulisem

    ulisem

    Joined:
    Jul 2, 2018
    Posts:
    1
    hi

    I found this solution by modifying in my case the Cinemachine FreeLook

    I added


    using UnityEngine.EventSystems;
    and the next function.
    private bool IsMouseOverUI()
    {
    return EventSystem.current.IsPointerOverGameObject();
    }

    this serves to detect if the mouse is on a GameObject of type UI.
    It works for me in play mode and on some occasion already generating the application in android but in recent times it no longer works in the android application.
    I don't know what's happening.

    I hope serve something

    **important note: it is necessary to copy and paste the CinemachineFreeLook script every time the project is closed and opened as the CinemachineFreelook script automatically changes back to its original state.


    Code (CSharp):
    1. using UnityEngine;
    2. using Cinemachine.Utility;
    3. using UnityEngine.Serialization;
    4. using System;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7. using UnityEngine.EventSystems;
    8.  
    9. namespace Cinemachine
    10. {
    11.     /// <summary>
    12.     /// A Cinemachine Camera geared towards a 3rd person camera experience.
    13.     /// The camera orbits around its subject with three separate camera rigs defining
    14.     /// rings around the target. Each rig has its own radius, height offset, composer,
    15.     /// and lens settings.
    16.     /// Depending on the camera's position along the spline connecting these three rigs,
    17.     /// these settings are interpolated to give the final camera position and state.
    18.     /// </summary>
    19.     [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
    20.     [DisallowMultipleComponent]
    21. #if UNITY_2018_3_OR_NEWER
    22.     [ExecuteAlways]
    23. #else
    24.     [ExecuteInEditMode]
    25. #endif
    26.     [AddComponentMenu("Cinemachine/CinemachineFreeLook")]
    27.     public class CinemachineFreeLook : CinemachineVirtualCameraBase
    28.     {
    29.         /// <summary>Object for the camera children to look at (the aim target)</summary>
    30.         [Tooltip("Object for the camera children to look at (the aim target).")]
    31.         [NoSaveDuringPlay]
    32.         public Transform m_LookAt = null;
    33.  
    34.         /// <summary>Object for the camera children wants to move with (the body target)</summary>
    35.         [Tooltip("Object for the camera children wants to move with (the body target).")]
    36.         [NoSaveDuringPlay]
    37.         public Transform m_Follow = null;
    38.  
    39.         /// <summary>If enabled, this lens setting will apply to all three child rigs, otherwise the child rig lens settings will be used</summary>
    40.         [Tooltip("If enabled, this lens setting will apply to all three child rigs, otherwise the child rig lens settings will be used")]
    41.         [FormerlySerializedAs("m_UseCommonLensSetting")]
    42.         public bool m_CommonLens = true;
    43.  
    44.         /// <summary>Specifies the lens properties of this Virtual Camera.
    45.         /// This generally mirrors the Unity Camera's lens settings, and will be used to drive
    46.         /// the Unity camera when the vcam is active</summary>
    47.         [FormerlySerializedAs("m_LensAttributes")]
    48.         [Tooltip("Specifies the lens properties of this Virtual Camera.  This generally mirrors the Unity Camera's lens settings, and will be used to drive the Unity camera when the vcam is active")]
    49.         [LensSettingsProperty]
    50.         public LensSettings m_Lens = LensSettings.Default;
    51.  
    52.         /// <summary> Collection of parameters that influence how this virtual camera transitions from
    53.         /// other virtual cameras </summary>
    54.         public TransitionParams m_Transitions;
    55.  
    56.         /// <summary>Legacy support</summary>
    57.         [SerializeField]
    58.         [HideInInspector]
    59.         [FormerlySerializedAs("m_BlendHint")]
    60.         [FormerlySerializedAs("m_PositionBlending")] private BlendHint m_LegacyBlendHint;
    61.  
    62.         /// <summary>The Vertical axis.  Value is 0..1.  Chooses how to blend the child rigs</summary>
    63.         [Header("Axis Control")]
    64.         [Tooltip("The Vertical axis.  Value is 0..1.  Chooses how to blend the child rigs")]
    65.         [AxisStateProperty]
    66.         public AxisState m_YAxis = new AxisState(0, 1, false, true, 2f, 0.2f, 0.1f, "Mouse Y", false);
    67.  
    68.         /// <summary>Controls how automatic recentering of the Y axis is accomplished</summary>
    69.         [Tooltip("Controls how automatic recentering of the Y axis is accomplished")]
    70.         public AxisState.Recentering m_YAxisRecentering = new AxisState.Recentering(false, 1, 2);
    71.  
    72.         /// <summary>The Horizontal axis.  Value is -180...180.  This is passed on to the rigs' OrbitalTransposer component</summary>
    73.         [Tooltip("The Horizontal axis.  Value is -180...180.  This is passed on to the rigs' OrbitalTransposer component")]
    74.         [AxisStateProperty]
    75.         public AxisState m_XAxis = new AxisState(-180, 180, true, false, 300f, 0.1f, 0.1f, "Mouse X", true);
    76.  
    77.         /// <summary>The definition of Forward.  Camera will follow behind</summary>
    78.         [OrbitalTransposerHeadingProperty]
    79.         [Tooltip("The definition of Forward.  Camera will follow behind.")]
    80.         public CinemachineOrbitalTransposer.Heading m_Heading
    81.             = new CinemachineOrbitalTransposer.Heading(
    82.                 CinemachineOrbitalTransposer.Heading.HeadingDefinition.TargetForward, 4, 0);
    83.  
    84.         /// <summary>Controls how automatic recentering of the X axis is accomplished</summary>
    85.         [Tooltip("Controls how automatic recentering of the X axis is accomplished")]
    86.         public AxisState.Recentering m_RecenterToTargetHeading = new AxisState.Recentering(false, 1, 2);
    87.  
    88.         /// <summary>The coordinate space to use when interpreting the offset from the target</summary>
    89.         [Header("Orbits")]
    90.         [Tooltip("The coordinate space to use when interpreting the offset from the target.  This is also used to set the camera's Up vector, which will be maintained when aiming the camera.")]
    91.         public CinemachineOrbitalTransposer.BindingMode m_BindingMode
    92.             = CinemachineOrbitalTransposer.BindingMode.SimpleFollowWithWorldUp;
    93.  
    94.         /// <summary></summary>
    95.         [Tooltip("Controls how taut is the line that connects the rigs' orbits, which determines final placement on the Y axis")]
    96.         [Range(0f, 1f)]
    97.         [FormerlySerializedAs("m_SplineTension")]
    98.         public float m_SplineCurvature = 0.2f;
    99.  
    100.         /// <summary>Defines the height and radius of the Rig orbit</summary>
    101.         [Serializable]
    102.         public struct Orbit
    103.         {
    104.             /// <summary>Height relative to target</summary>
    105.             public float m_Height;
    106.             /// <summary>Radius of orbit</summary>
    107.             public float m_Radius;
    108.             /// <summary>Constructor with specific values</summary>
    109.             public Orbit(float h, float r) { m_Height = h; m_Radius = r; }
    110.         }
    111.  
    112.         /// <summary>The radius and height of the three orbiting rigs</summary>
    113.         [Tooltip("The radius and height of the three orbiting rigs.")]
    114.         public Orbit[] m_Orbits = new Orbit[3]
    115.         {
    116.             // These are the default orbits
    117.             new Orbit(4.5f, 1.75f),
    118.             new Orbit(2.5f, 3f),
    119.             new Orbit(0.4f, 1.3f)
    120.         };
    121.  
    122.         // Legacy support
    123.         [SerializeField]
    124.         [HideInInspector]
    125.         [FormerlySerializedAs("m_HeadingBias")]
    126.         private float m_LegacyHeadingBias = float.MaxValue;
    127.         bool mUseLegacyRigDefinitions = false;
    128.  
    129.         /// <summary>Enforce bounds for fields, when changed in inspector.</summary>
    130.         protected override void OnValidate()
    131.         {
    132.             base.OnValidate();
    133.  
    134.             // Upgrade after a legacy deserialize
    135.             if (m_LegacyHeadingBias != float.MaxValue)
    136.             {
    137.                 m_Heading.m_Bias = m_LegacyHeadingBias;
    138.                 m_LegacyHeadingBias = float.MaxValue;
    139.                 int heading = (int)m_Heading.m_Definition;
    140.                 if (m_RecenterToTargetHeading.LegacyUpgrade(ref heading, ref m_Heading.m_VelocityFilterStrength))
    141.                     m_Heading.m_Definition = (CinemachineOrbitalTransposer.Heading.HeadingDefinition)heading;
    142.                 mUseLegacyRigDefinitions = true;
    143.             }
    144.             if (m_LegacyBlendHint != BlendHint.None)
    145.             {
    146.                 m_Transitions.m_BlendHint = m_LegacyBlendHint;
    147.                 m_LegacyBlendHint = BlendHint.None;
    148.             }
    149.             m_YAxis.Validate();
    150.             m_XAxis.Validate();
    151.             m_RecenterToTargetHeading.Validate();
    152.             m_YAxisRecentering.Validate();
    153.             m_Lens.Validate();
    154.  
    155.             InvalidateRigCache();
    156.         }
    157.  
    158.         /// <summary>Get a child rig</summary>
    159.         /// <param name="i">Rig index.  Can be 0, 1, or 2</param>
    160.         /// <returns>The rig, or null if index is bad.</returns>
    161.         public CinemachineVirtualCamera GetRig(int i)
    162.         {
    163.             UpdateRigCache();
    164.             return (i < 0 || i > 2) ? null : m_Rigs[i];
    165.         }
    166.  
    167.  
    168.  
    169.  
    170.         /// <summary>Names of the 3 child rigs</summary>
    171.         public static string[] RigNames { get { return new string[] { "TopRig", "MiddleRig", "BottomRig" }; } }
    172.  
    173.         bool mIsDestroyed = false;
    174.  
    175.         /// <summary>Updates the child rig cache</summary>
    176.         protected override void OnEnable()
    177.         {
    178.             mIsDestroyed = false;
    179.             base.OnEnable();
    180.             InvalidateRigCache();
    181.         }
    182.  
    183.         /// <summary>Makes sure that the child rigs get destroyed in an undo-firndly manner.
    184.         /// Invalidates the rig cache.</summary>
    185.         protected override void OnDestroy()
    186.         {
    187.             // Make the rigs visible instead of destroying - this is to keep Undo happy
    188.             if (m_Rigs != null)
    189.                 foreach (var rig in m_Rigs)
    190.                     if (rig != null && rig.gameObject != null)
    191.                         rig.gameObject.hideFlags
    192.                             &= ~(HideFlags.HideInHierarchy | HideFlags.HideInInspector);
    193.  
    194.             mIsDestroyed = true;
    195.             base.OnDestroy();
    196.         }
    197.  
    198.         /// <summary>Invalidates the rig cache</summary>
    199.         void OnTransformChildrenChanged()
    200.         {
    201.             InvalidateRigCache();
    202.         }
    203.  
    204.         void Reset()
    205.         {
    206.             DestroyRigs();
    207.         }
    208.  
    209.         /// <summary>The cacmera state, which will be a blend of the child rig states</summary>
    210.         override public CameraState State { get { return m_State; } }
    211.  
    212.         /// <summary>Get the current LookAt target.  Returns parent's LookAt if parent
    213.         /// is non-null and no specific LookAt defined for this camera</summary>
    214.         override public Transform LookAt
    215.         {
    216.             get { return ResolveLookAt(m_LookAt); }
    217.             set { m_LookAt = value; }
    218.         }
    219.  
    220.         /// <summary>Get the current Follow target.  Returns parent's Follow if parent
    221.         /// is non-null and no specific Follow defined for this camera</summary>
    222.         override public Transform Follow
    223.         {
    224.             get { return ResolveFollow(m_Follow); }
    225.             set { m_Follow = value; }
    226.         }
    227.  
    228.         /// <summary>Check whether the vcam a live child of this camera.
    229.         /// Returns true if the child is currently contributing actively to the camera state.</summary>
    230.         /// <param name="vcam">The Virtual Camera to check</param>
    231.         /// <param name="dominantChildOnly">If truw, will only return true if this vcam is the dominat live child</param>
    232.         /// <returns>True if the vcam is currently actively influencing the state of this vcam</returns>
    233.         public override bool IsLiveChild(ICinemachineCamera vcam, bool dominantChildOnly = false)
    234.         {
    235.             // Do not update the rig cache here or there will be infinite loop at creation time
    236.             if (m_Rigs == null || m_Rigs.Length != 3)
    237.                 return false;
    238.             var y = GetYAxisValue();
    239.             if (dominantChildOnly)
    240.             {
    241.                 if (vcam == (ICinemachineCamera)m_Rigs[0])
    242.                     return y > 0.666f;
    243.                 if (vcam == (ICinemachineCamera)m_Rigs[2])
    244.                     return y < 0.333;
    245.                 if (vcam == (ICinemachineCamera)m_Rigs[1])
    246.                     return y >= 0.333f && y <= 0.666f;
    247.                 return false;
    248.             }
    249.             if (vcam == (ICinemachineCamera)m_Rigs[1])
    250.                 return true;
    251.             if (y < 0.5f)
    252.                 return vcam == (ICinemachineCamera)m_Rigs[2];
    253.             return vcam == (ICinemachineCamera)m_Rigs[0];
    254.         }
    255.  
    256.         /// <summary>This is called to notify the vcam that a target got warped,
    257.         /// so that the vcam can update its internal state to make the camera
    258.         /// also warp seamlessy.</summary>
    259.         /// <param name="target">The object that was warped</param>
    260.         /// <param name="positionDelta">The amount the target's position changed</param>
    261.         public override void OnTargetObjectWarped(Transform target, Vector3 positionDelta)
    262.         {
    263.             UpdateRigCache();
    264.             if (m_Rigs != null)
    265.                 foreach (var vcam in m_Rigs)
    266.                     vcam.OnTargetObjectWarped(target, positionDelta);
    267.             base.OnTargetObjectWarped(target, positionDelta);
    268.         }
    269.  
    270.         /// <summary>Internal use only.  Called by CinemachineCore at designated update time
    271.         /// so the vcam can position itself and track its targets.  All 3 child rigs are updated,
    272.         /// and a blend calculated, depending on the value of the Y axis.</summary>
    273.         /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
    274.         /// <param name="deltaTime">Delta time for time-based effects (ignore if less than 0)</param>
    275.         override public void InternalUpdateCameraState(Vector3 worldUp, float deltaTime)
    276.         {
    277.             if (!PreviousStateIsValid)
    278.                 deltaTime = -1;
    279.  
    280.             UpdateRigCache();
    281.  
    282.             // Update the current state by invoking the component pipeline
    283.             m_State = CalculateNewState(worldUp, deltaTime);
    284.             ApplyPositionBlendMethod(ref m_State, m_Transitions.m_BlendHint);
    285.  
    286.             // Push the raw position back to the game object's transform, so it
    287.             // moves along with the camera.  Leave the orientation alone, because it
    288.             // screws up camera dragging when there is a LookAt behaviour.
    289.             if (Follow != null)
    290.             {
    291.                 Vector3 delta = State.RawPosition - transform.position;
    292.                 transform.position = State.RawPosition;
    293.                 m_Rigs[0].transform.position -= delta;
    294.                 m_Rigs[1].transform.position -= delta;
    295.                 m_Rigs[2].transform.position -= delta;
    296.             }
    297.  
    298.             InvokePostPipelineStageCallback(this, CinemachineCore.Stage.Finalize, ref m_State, deltaTime);
    299.             PreviousStateIsValid = true;
    300.  
    301.             // Set up for next frame
    302.             bool activeCam = (deltaTime >= 0) && CinemachineCore.Instance.IsLive(this);
    303.             if (activeCam)
    304.             {
    305.                 if (m_YAxis.Update(deltaTime))
    306.                     m_YAxisRecentering.CancelRecentering();
    307.             }
    308.             PushSettingsToRigs();
    309.             if (m_BindingMode == CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp)
    310.                 m_XAxis.Value = 0;
    311.         }
    312.  
    313.         /// <summary>If we are transitioning from another FreeLook, grab the axis values from it.</summary>
    314.         /// <param name="fromCam">The camera being deactivated.  May be null.</param>
    315.         /// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
    316.         /// <param name="deltaTime">Delta time for time-based effects (ignore if less than or equal to 0)</param>
    317.         public override void OnTransitionFromCamera(
    318.             ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime)
    319.         {
    320.             base.OnTransitionFromCamera(fromCam, worldUp, deltaTime);
    321.             bool forceUpdate = false;
    322.             if (fromCam != null && m_Transitions.m_InheritPosition)
    323.             {
    324.                 var cameraPos = fromCam.State.RawPosition;
    325.  
    326.                 // Special handling for FreeLook: get an undamped outgoing position
    327.                 if (fromCam is CinemachineFreeLook)
    328.                 {
    329.                     var flFrom = (fromCam as CinemachineFreeLook);
    330.                     var orbital = flFrom.mOrbitals != null ? flFrom.mOrbitals[1] : null;
    331.                     if (orbital != null)
    332.                         cameraPos = orbital.GetTargetCameraPosition(worldUp);
    333.                 }
    334.                 UpdateRigCache();
    335.                 if (m_BindingMode != CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp)
    336.                     m_XAxis.Value = mOrbitals[1].GetAxisClosestValue(cameraPos, worldUp);
    337.                 m_YAxis.Value = GetYAxisClosestValue(cameraPos, worldUp);
    338.  
    339.                 transform.position = cameraPos;
    340.                 m_State = PullStateFromVirtualCamera(worldUp, ref m_Lens);
    341.                 PreviousStateIsValid = false;
    342.                 PushSettingsToRigs();
    343.                 forceUpdate = true;
    344.             }
    345.             if (forceUpdate)
    346.                 InternalUpdateCameraState(worldUp, deltaTime);
    347.             else
    348.                 UpdateCameraState(worldUp, deltaTime);
    349.             if (m_Transitions.m_OnCameraLive != null)
    350.                 m_Transitions.m_OnCameraLive.Invoke(this, fromCam);
    351.         }
    352.  
    353.         float GetYAxisClosestValue(Vector3 cameraPos, Vector3 up)
    354.         {
    355.             if (Follow != null)
    356.             {
    357.                 // Rotate the camera pos to the back
    358.                 Quaternion q = Quaternion.FromToRotation(up, Vector3.up);
    359.                 Vector3 dir = q * (cameraPos - Follow.position);
    360.                 Vector3 flatDir = dir; flatDir.y = 0;
    361.                 if (!flatDir.AlmostZero())
    362.                 {
    363.                     float angle = Vector3.SignedAngle(flatDir, Vector3.back, Vector3.up);
    364.                     dir = Quaternion.AngleAxis(angle, Vector3.up) * dir;
    365.                 }
    366.                 dir.x = 0;
    367.  
    368.                 // Sample the spline in a few places, find the 2 closest, and lerp
    369.                 int i0 = 0, i1 = 0;
    370.                 float a0 = 0, a1 = 0;
    371.                 const int NumSamples = 13;
    372.                 float step = 1f / (NumSamples - 1);
    373.                 for (int i = 0; i < NumSamples; ++i)
    374.                 {
    375.                     float a = Vector3.SignedAngle(
    376.                         dir, GetLocalPositionForCameraFromInput(i * step), Vector3.right);
    377.                     if (i == 0)
    378.                         a0 = a1 = a;
    379.                     else
    380.                     {
    381.                         if (Mathf.Abs(a) < Mathf.Abs(a0))
    382.                         {
    383.                             a1 = a0;
    384.                             i1 = i0;
    385.                             a0 = a;
    386.                             i0 = i;
    387.                         }
    388.                         else if (Mathf.Abs(a) < Mathf.Abs(a1))
    389.                         {
    390.                             a1 = a;
    391.                             i1 = i;
    392.                         }
    393.                     }
    394.                 }
    395.                 if (Mathf.Sign(a0) == Mathf.Sign(a1))
    396.                     return i0 * step;
    397.                 float t = Mathf.Abs(a0) / (Mathf.Abs(a0) + Mathf.Abs(a1));
    398.                 return Mathf.Lerp(i0 * step, i1 * step, t);
    399.             }
    400.             return m_YAxis.Value; // stay conservative
    401.         }
    402.  
    403.         CameraState m_State = CameraState.Default;          // Current state this frame
    404.  
    405.         /// Serialized in order to support copy/paste
    406.         [SerializeField]
    407.         [HideInInspector]
    408.         [NoSaveDuringPlay]
    409.         private CinemachineVirtualCamera[] m_Rigs
    410.             = new CinemachineVirtualCamera[3];
    411.  
    412.         void InvalidateRigCache() { mOrbitals = null; }
    413.         CinemachineOrbitalTransposer[] mOrbitals = null;
    414.         CinemachineBlend mBlendA;
    415.         CinemachineBlend mBlendB;
    416.  
    417.         /// <summary>
    418.         /// Override component pipeline creation.
    419.         /// This needs to be done by the editor to support Undo.
    420.         /// The override must do exactly the same thing as the CreatePipeline method in this class.
    421.         /// </summary>
    422.         public static CreateRigDelegate CreateRigOverride;
    423.  
    424.         /// <summary>
    425.         /// Override component pipeline creation.
    426.         /// This needs to be done by the editor to support Undo.
    427.         /// The override must do exactly the same thing as the CreatePipeline method in this class.
    428.         /// </summary>
    429.         public delegate CinemachineVirtualCamera CreateRigDelegate(
    430.             CinemachineFreeLook vcam, string name, CinemachineVirtualCamera copyFrom);
    431.  
    432.         /// <summary>
    433.         /// Override component pipeline destruction.
    434.         /// This needs to be done by the editor to support Undo.
    435.         /// </summary>
    436.         public static DestroyRigDelegate DestroyRigOverride;
    437.  
    438.         /// <summary>
    439.         /// Override component pipeline destruction.
    440.         /// This needs to be done by the editor to support Undo.
    441.         /// </summary>
    442.         public delegate void DestroyRigDelegate(GameObject rig);
    443.  
    444.         private void DestroyRigs()
    445.         {
    446.             CinemachineVirtualCamera[] oldRigs = new CinemachineVirtualCamera[RigNames.Length];
    447.             for (int i = 0; i < RigNames.Length; ++i)
    448.             {
    449.                 foreach (Transform child in transform)
    450.                     if (child.gameObject.name == RigNames[i])
    451.                         oldRigs[i] = child.GetComponent<CinemachineVirtualCamera>();
    452.             }
    453.             for (int i = 0; i < oldRigs.Length; ++i)
    454.             {
    455.                 if (oldRigs[i] != null)
    456.                 {
    457.                     if (DestroyRigOverride != null)
    458.                         DestroyRigOverride(oldRigs[i].gameObject);
    459.                     else
    460.                         Destroy(oldRigs[i].gameObject);
    461.                 }
    462.             }
    463.             m_Rigs = null;
    464.             mOrbitals = null;
    465.         }
    466.  
    467.         private CinemachineVirtualCamera[] CreateRigs(CinemachineVirtualCamera[] copyFrom)
    468.         {
    469.             // Invalidate the cache
    470.             mOrbitals = null;
    471.             float[] softCenterDefaultsV = new float[] { 0.5f, 0.55f, 0.6f };
    472.             CinemachineVirtualCamera[] newRigs = new CinemachineVirtualCamera[3];
    473.             for (int i = 0; i < RigNames.Length; ++i)
    474.             {
    475.                 CinemachineVirtualCamera src = null;
    476.                 if (copyFrom != null && copyFrom.Length > i)
    477.                     src = copyFrom[i];
    478.  
    479.                 if (CreateRigOverride != null)
    480.                     newRigs[i] = CreateRigOverride(this, RigNames[i], src);
    481.                 else
    482.                 {
    483.                     // Create a new rig with default components
    484.                     // Note: copyFrom only supported in Editor, not build
    485.                     GameObject go = new GameObject(RigNames[i]);
    486.                     go.transform.parent = transform;
    487.                     newRigs[i] = go.AddComponent<CinemachineVirtualCamera>();
    488.                     go = newRigs[i].GetComponentOwner().gameObject;
    489.                     go.AddComponent<CinemachineOrbitalTransposer>();
    490.                     go.AddComponent<CinemachineComposer>();
    491.                 }
    492.  
    493.                 // Set up the defaults
    494.                 newRigs[i].InvalidateComponentPipeline();
    495.                 CinemachineOrbitalTransposer orbital = newRigs[i].GetCinemachineComponent<CinemachineOrbitalTransposer>();
    496.                 if (orbital == null)
    497.                     orbital = newRigs[i].AddCinemachineComponent<CinemachineOrbitalTransposer>(); // should not happen
    498.                 if (src == null)
    499.                 {
    500.                     // Only set defaults if not copying
    501.                     orbital.m_YawDamping = 0;
    502.                     CinemachineComposer composer = newRigs[i].GetCinemachineComponent<CinemachineComposer>();
    503.                     if (composer != null)
    504.                     {
    505.                         composer.m_HorizontalDamping = composer.m_VerticalDamping = 0;
    506.                         composer.m_ScreenX = 0.5f;
    507.                         composer.m_ScreenY = softCenterDefaultsV[i];
    508.                         composer.m_DeadZoneWidth = composer.m_DeadZoneHeight = 0f;
    509.                         composer.m_SoftZoneWidth = composer.m_SoftZoneHeight = 0.8f;
    510.                         composer.m_BiasX = composer.m_BiasY = 0;
    511.                     }
    512.                 }
    513.             }
    514.             return newRigs;
    515.         }
    516.  
    517.         private void UpdateRigCache()
    518.         {
    519.             if (mIsDestroyed)
    520.                 return;
    521.  
    522.             bool isPrefab = false;
    523.  
    524. #if UNITY_EDITOR
    525.             // Special condition: Did we just get copy/pasted?
    526.             if (m_Rigs != null && m_Rigs.Length == 3
    527.                 && m_Rigs[0] != null && m_Rigs[0].transform.parent != transform)
    528.             {
    529.                 isPrefab = gameObject.scene.name == null; // causes a small GC alloc
    530.                 if (!isPrefab) // can't paste to a prefab
    531.                 {
    532.                     var copyFrom = m_Rigs;
    533.                     DestroyRigs();
    534.                     m_Rigs = CreateRigs(copyFrom);
    535.                 }
    536.             }
    537.             for (int i = 0; m_Rigs != null && i < 3; ++i)
    538.                 if (m_Rigs[i] != null)
    539.                     CinemachineVirtualCamera.SetFlagsForHiddenChild(m_Rigs[i].gameObject);
    540. #endif
    541.  
    542.             // Early out if we're up to date
    543.             if (mOrbitals != null && mOrbitals.Length == 3)
    544.                 return;
    545.  
    546.             // Locate existing rigs, and recreate them if any are missing
    547.             isPrefab = gameObject.scene.name == null; // causes a small GC alloc
    548.             if (LocateExistingRigs(RigNames, false) != 3 && !isPrefab)
    549.             {
    550.                 DestroyRigs();
    551.                 m_Rigs = CreateRigs(null);
    552.                 LocateExistingRigs(RigNames, true);
    553.             }
    554.  
    555.             foreach (var rig in m_Rigs)
    556.             {
    557.                 // Configure the UI
    558.                 rig.m_ExcludedPropertiesInInspector = m_CommonLens
    559.                     ? new string[] { "m_Script", "Header", "Extensions", "m_Priority", "m_Transitions", "m_Follow", "m_StandbyUpdate", "m_Lens" }
    560.                     : new string[] { "m_Script", "Header", "Extensions", "m_Priority", "m_Transitions", "m_Follow", "m_StandbyUpdate" };
    561.                 rig.m_LockStageInInspector = new CinemachineCore.Stage[] { CinemachineCore.Stage.Body };
    562.             }
    563.  
    564.             // Create the blend objects
    565.             mBlendA = new CinemachineBlend(m_Rigs[1], m_Rigs[0], AnimationCurve.Linear(0, 0, 1, 1), 1, 0);
    566.             mBlendB = new CinemachineBlend(m_Rigs[2], m_Rigs[1], AnimationCurve.Linear(0, 0, 1, 1), 1, 0);
    567.         }
    568.  
    569.         private int LocateExistingRigs(string[] rigNames, bool forceOrbital)
    570.         {
    571.             mOrbitals = new CinemachineOrbitalTransposer[rigNames.Length];
    572.             m_Rigs = new CinemachineVirtualCamera[rigNames.Length];
    573.             int rigsFound = 0;
    574.             foreach (Transform child in transform)
    575.             {
    576.                 CinemachineVirtualCamera vcam = child.GetComponent<CinemachineVirtualCamera>();
    577.                 if (vcam != null)
    578.                 {
    579.                     GameObject go = child.gameObject;
    580.                     for (int i = 0; i < rigNames.Length; ++i)
    581.                     {
    582.                         if (mOrbitals[i] == null && go.name == rigNames[i])
    583.                         {
    584.                             // Must have an orbital transposer or it's no good
    585.                             mOrbitals[i] = vcam.GetCinemachineComponent<CinemachineOrbitalTransposer>();
    586.                             if (mOrbitals[i] == null && forceOrbital)
    587.                                 mOrbitals[i] = vcam.AddCinemachineComponent<CinemachineOrbitalTransposer>();
    588.                             if (mOrbitals[i] != null)
    589.                             {
    590.                                 mOrbitals[i].m_HeadingIsSlave = true;
    591.                                 if (i == 1)
    592.                                     mOrbitals[i].HeadingUpdater = UpdateXAxisHeading;
    593.                                 m_Rigs[i] = vcam;
    594.                                 m_Rigs[i].m_StandbyUpdate = m_StandbyUpdate;
    595.                                 ++rigsFound;
    596.                             }
    597.                         }
    598.                     }
    599.                 }
    600.             }
    601.             return rigsFound;
    602.         }
    603.  
    604.         float UpdateXAxisHeading(CinemachineOrbitalTransposer orbital, float deltaTime, Vector3 up)
    605.         {
    606.             if (Application.isPlaying)
    607.             {
    608.                 if (!IsMouseOverUI())
    609.             {
    610.                 var oldValue = m_XAxis.Value;
    611.                 float headng = orbital.UpdateHeading(deltaTime, up, ref m_XAxis);
    612.                 // Allow externally-driven values to work in this mode
    613.                 if (orbital.m_BindingMode == CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp)
    614.                     m_XAxis.Value = oldValue;
    615.                 return headng;
    616.             }
    617.             }
    618.  
    619.             return m_XAxis.Value;
    620.         }
    621.  
    622.         void PushSettingsToRigs()
    623.         {
    624.             UpdateRigCache();
    625.             for (int i = 0; i < m_Rigs.Length; ++i)
    626.             {
    627.                 if (m_Rigs[i] == null)
    628.                     continue;
    629.  
    630.                 if (m_CommonLens)
    631.                     m_Rigs[i].m_Lens = m_Lens;
    632.  
    633.                 // If we just deserialized from a legacy version,
    634.                 // pull the orbits and targets from the rigs
    635.                 if (mUseLegacyRigDefinitions)
    636.                 {
    637.                     mUseLegacyRigDefinitions = false;
    638.                     m_Orbits[i].m_Height = mOrbitals[i].m_FollowOffset.y;
    639.                     m_Orbits[i].m_Radius = -mOrbitals[i].m_FollowOffset.z;
    640.                     if (m_Rigs[i].Follow != null)
    641.                         Follow = m_Rigs[i].Follow;
    642.                 }
    643.                 m_Rigs[i].Follow = null;
    644.                 m_Rigs[i].m_StandbyUpdate = m_StandbyUpdate;
    645.                 if (!PreviousStateIsValid)
    646.                 {
    647.                     m_Rigs[i].PreviousStateIsValid = false;
    648.                     m_Rigs[i].transform.position = transform.position;
    649.                     m_Rigs[i].transform.rotation = transform.rotation;
    650.                 }
    651.                 // Hide the rigs from prying eyes
    652.                 CinemachineVirtualCamera.SetFlagsForHiddenChild(m_Rigs[i].gameObject);
    653.  
    654.                 mOrbitals[i].m_FollowOffset = GetLocalPositionForCameraFromInput(GetYAxisValue());
    655.                 mOrbitals[i].m_BindingMode = m_BindingMode;
    656.                 mOrbitals[i].m_Heading = m_Heading;
    657.                 mOrbitals[i].m_XAxis = m_XAxis;
    658.                 mOrbitals[i].m_RecenterToTargetHeading.m_enabled = (i == 1) ? m_RecenterToTargetHeading.m_enabled : false;
    659.                 mOrbitals[i].m_RecenterToTargetHeading.m_WaitTime = m_RecenterToTargetHeading.m_WaitTime;
    660.                 mOrbitals[i].m_RecenterToTargetHeading.m_RecenteringTime = m_RecenterToTargetHeading.m_RecenteringTime;
    661.                 mOrbitals[i].m_RecenterToTargetHeading.CopyStateFrom(ref m_RecenterToTargetHeading);
    662.  
    663.                 // Hack to get SimpleFollow with heterogeneous dampings to work
    664.                 if (m_BindingMode == CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp)
    665.                     m_Rigs[i].SetStateRawPosition(State.RawPosition);
    666.             }
    667.         }
    668.  
    669.         private float GetYAxisValue()
    670.         {
    671.             if (Application.isPlaying)
    672.             {
    673.                 if (!IsMouseOverUI())
    674.                 {
    675.                 float range = m_YAxis.m_MaxValue - m_YAxis.m_MinValue;
    676.                 return (range > UnityVectorExtensions.Epsilon) ? m_YAxis.Value / range : 0.5f;
    677.                 }
    678.             }
    679.             return m_YAxis.Value;
    680.         }
    681.  
    682.         private CameraState CalculateNewState(Vector3 worldUp, float deltaTime)
    683.         {
    684.             CameraState state = PullStateFromVirtualCamera(worldUp, ref m_Lens);
    685.  
    686.             m_YAxisRecentering.DoRecentering(ref m_YAxis, deltaTime, 0.5f);
    687.  
    688.             // Blend from the appropriate rigs
    689.             float t = GetYAxisValue();
    690.             if (t > 0.5f)
    691.             {
    692.                 if (mBlendA != null)
    693.                 {
    694.                     mBlendA.TimeInBlend = (t - 0.5f) * 2f;
    695.                     mBlendA.UpdateCameraState(worldUp, deltaTime);
    696.                     state = mBlendA.State;
    697.                 }
    698.             }
    699.             else
    700.             {
    701.                 if (mBlendB != null)
    702.                 {
    703.                     mBlendB.TimeInBlend = t * 2f;
    704.                     mBlendB.UpdateCameraState(worldUp, deltaTime);
    705.                     state = mBlendB.State;
    706.                 }
    707.             }
    708.             return state;
    709.         }
    710.  
    711.         /// <summary>
    712.         /// Returns the local position of the camera along the spline used to connect the
    713.         /// three camera rigs. Does not take into account the current heading of the
    714.         /// camera (or its target)
    715.         /// </summary>
    716.         /// <param name="t">The t-value for the camera on its spline. Internally clamped to
    717.         /// the value [0,1]</param>
    718.         /// <returns>The local offset (back + up) of the camera WRT its target based on the
    719.         /// supplied t-value</returns>
    720.         public Vector3 GetLocalPositionForCameraFromInput(float t)
    721.         {
    722.             if (mOrbitals == null)
    723.                 return Vector3.zero;
    724.             UpdateCachedSpline();
    725.             int n = 1;
    726.             if (t > 0.5f)
    727.             {
    728.                 t -= 0.5f;
    729.                 n = 2;
    730.             }
    731.             return SplineHelpers.Bezier3(
    732.                 t * 2f, m_CachedKnots[n], m_CachedCtrl1[n], m_CachedCtrl2[n], m_CachedKnots[n + 1]);
    733.         }
    734.  
    735.  
    736.  
    737.         private bool IsMouseOverUI()
    738.         {
    739.             return EventSystem.current.IsPointerOverGameObject();
    740.         }
    741.  
    742.         Orbit[] m_CachedOrbits;
    743.         float m_CachedTension;
    744.         Vector4[] m_CachedKnots;
    745.         Vector4[] m_CachedCtrl1;
    746.         Vector4[] m_CachedCtrl2;
    747.         void UpdateCachedSpline()
    748.         {
    749.             bool cacheIsValid = (m_CachedOrbits != null && m_CachedTension == m_SplineCurvature);
    750.             for (int i = 0; i < 3 && cacheIsValid; ++i)
    751.                 cacheIsValid = (m_CachedOrbits[i].m_Height == m_Orbits[i].m_Height
    752.                     && m_CachedOrbits[i].m_Radius == m_Orbits[i].m_Radius);
    753.             if (!cacheIsValid)
    754.             {
    755.                 float t = m_SplineCurvature;
    756.                 m_CachedKnots = new Vector4[5];
    757.                 m_CachedCtrl1 = new Vector4[5];
    758.                 m_CachedCtrl2 = new Vector4[5];
    759.                 m_CachedKnots[1] = new Vector4(0, m_Orbits[2].m_Height, -m_Orbits[2].m_Radius, 0);
    760.                 m_CachedKnots[2] = new Vector4(0, m_Orbits[1].m_Height, -m_Orbits[1].m_Radius, 0);
    761.                 m_CachedKnots[3] = new Vector4(0, m_Orbits[0].m_Height, -m_Orbits[0].m_Radius, 0);
    762.                 m_CachedKnots[0] = Vector4.Lerp(m_CachedKnots[1], Vector4.zero, t);
    763.                 m_CachedKnots[4] = Vector4.Lerp(m_CachedKnots[3], Vector4.zero, t);
    764.                 SplineHelpers.ComputeSmoothControlPoints(
    765.                     ref m_CachedKnots, ref m_CachedCtrl1, ref m_CachedCtrl2);
    766.                 m_CachedOrbits = new Orbit[3];
    767.                 for (int i = 0; i < 3; ++i)
    768.                     m_CachedOrbits[i] = m_Orbits[i];
    769.                 m_CachedTension = m_SplineCurvature;
    770.             }
    771.         }
    772.     }
    773. }
    774.  
     
    sacb0y likes this.