Search Unity

Is it possible to check within CinemachineTransposer if it's the active camera?

Discussion in 'Cinemachine' started by roseportalgames, Jan 23, 2019.

  1. roseportalgames

    roseportalgames

    Joined:
    Aug 9, 2017
    Posts:
    173
    I need to be able to check whether the CinemachineTransposer script is on a camera that is currently Live. How can I go about doing this?

    I was thinking of retrieving the live camera from the CinemachineBrain, then checking if its CinemachineTransposer matches the one in the script, but it's not as easy as I had hoped.

    Basically I need this because I need to apply some code if a CinemachineTransposer is NOT live.

    Thanks a lot :)
     
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    I'm not sure I completely understand the question.

    Do you mean to check
    1. whether the currently live camera has a Transposer on it, or
    2. whether it has a Transposer with specific settings, or
    3. whether it's a specific vcam or family of vcams?
    Also, when you say Live, do you include vcams that are blending out? So if a non-transposer camera goes live, but there is a 2-second blend with the outgoing transposer cam, is the the outgoing cam still live for your purposes?
     
  3. roseportalgames

    roseportalgames

    Joined:
    Aug 9, 2017
    Posts:
    173
    I created my own version of Transposer, it's called "CinemachineTransposerCustom".

    The original CinemachineTransposer -- when a camera is not live -- it immediately "follows" the Target. Damping is ignored. I shouldn't even call it follow, because the camera position is simply always the Target's position.

    I've changed the damping function into SmoothDamp (from my other topic on this forum) for my own version.

    The problem now is that the camera SmoothDamps to its Target, even when the camera is not live. That's a problem for the way I am using the camera system, which is based on the original behavior.

    I was thinking about checking in CinemachineTransposerCustom if that script is on a CinemachineCamera that is NOT live. If it's not live, I will simply change the position always to the targetPosition. No SmoothDamp. That way I can replicate the original CinemachineTransposer behavior.



    When I say "not live", I mean also not blending. So not functional at all. I think IsLive is true if a camera is blending, right? So ! IsLive would be correct.
     
  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    ok, now I understand what you're trying to do. Thanks for the description.

    In the Transposer's MutateCameraState() function, deltaTime will be -1 when the vcam isn't live. Just check for that in your damping implementation.
     
    Whatever560 and roseportalgames like this.
  5. roseportalgames

    roseportalgames

    Joined:
    Aug 9, 2017
    Posts:
    173
    Awesome, thank you! :)

    Is it possible that there's a 2 frame delay between the live camera switching and deltaTime going from -1 to a normal value?

    Code (CSharp):
    1.         public override void MutateCameraState(ref CameraState curState, float deltaTime)
    2.         {
    3.             //UnityEngine.Profiling.Profiler.BeginSample("CinemachineTransposer.MutateCameraState");
    4.             InitPrevFrameStateInfo(ref curState, deltaTime);
    5.             if (IsValid)
    6.             {
    7.                 Vector3 pos;
    8.                 Quaternion orient;
    9.                 Vector3 offset = EffectiveOffset;
    10.                 TrackTarget(deltaTime, curState.ReferenceUp, offset, out pos, out orient);
    11.                 curState.RawPosition = pos + orient * offset;
    12.                 curState.ReferenceUp = orient * Vector3.up;
    13.             }
    14.             catchDeltaTime = deltaTime;
    15.             //UnityEngine.Profiling.Profiler.EndSample();
    16.         }
     
  6. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    I'm not aware of any such delay.
    What version of CM are you using? That code looks old.
     
  7. roseportalgames

    roseportalgames

    Joined:
    Aug 9, 2017
    Posts:
    173
    I'm using Cinemachine 2.2.7. However, that Transposer code might be outdated because I had to grab it from another source. I wasn't able to view the full Transposer code from the Assembly Browser to customize it. Full code at the bottom of this post.

    Quick side question regarding SmoothDamp, I'm using this line:

    Code (CSharp):
    1. Vector3 newPos = Vector3.SmoothDamp(currentPosition, targetPosition, ref velocity, 0.1F, maxSpeed);
    maxSpeed is a variable that I want to change depending on how fast I want the camera to move at that moment.

    However, it never changes the speed of the camera. It seems SmoothDamp only "reads and uses" the initial value of maxSpeed.

    If I place a Debug.Log(maxSpeed) right above that line, it will indeed show the current value (1, or 6, or 15). But changing the value weirdly has no effect at all on the speed?



    Code (CSharp):
    1. using Cinemachine.Utility;
    2. using UnityEngine;
    3.  
    4. namespace Cinemachine
    5. {
    6.     [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
    7.     [AddComponentMenu("")]
    8.     [RequireComponent(typeof(CinemachinePipeline))]
    9.     [SaveDuringPlay]
    10.     public class CinemachineTransposerConstant : CinemachineComponentBase
    11.     {
    12.         [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
    13.         public enum BindingMode
    14.         {
    15.             LockToTargetOnAssign,
    16.             LockToTargetWithWorldUp,
    17.             LockToTargetNoRoll,
    18.             LockToTarget,
    19.             WorldSpace,
    20.             SimpleFollowWithWorldUp
    21.         }
    22.  
    23.         [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.")]
    24.         public BindingMode m_BindingMode = BindingMode.LockToTargetWithWorldUp;
    25.  
    26.         /// <summary>The distance which the transposer will attempt to maintain from the transposer subject</summary>
    27.         [Tooltip("The distance vector that the transposer will attempt to maintain from the Follow target")]
    28.         public Vector3 m_FollowOffset = Vector3.back * 10f;
    29.  
    30.         [Range(0f, 20f)]
    31.         [Tooltip("How aggressively the camera tries to maintain the offset in the X-axis.  Small numbers are more responsive, rapidly translating the camera to keep the target's x-axis offset.  Larger numbers give a more heavy slowly responding camera. Using different settings per axis can yield a wide range of camera behaviors.")]
    32.         public float m_XDamping = 1f;
    33.  
    34.         [Range(0f, 20f)]
    35.         [Tooltip("How aggressively the camera tries to maintain the offset in the Y-axis.  Small numbers are more responsive, rapidly translating the camera to keep the target's y-axis offset.  Larger numbers give a more heavy slowly responding camera. Using different settings per axis can yield a wide range of camera behaviors.")]
    36.         public float m_YDamping = 1f;
    37.  
    38.         [Range(0f, 20f)]
    39.         [Tooltip("How aggressively the camera tries to maintain the offset in the Z-axis.  Small numbers are more responsive, rapidly translating the camera to keep the target's z-axis offset.  Larger numbers give a more heavy slowly responding camera. Using different settings per axis can yield a wide range of camera behaviors.")]
    40.         public float m_ZDamping = 1f;
    41.  
    42.         [Range(0f, 20f)]
    43.         [Tooltip("How aggressively the camera tries to track the target rotation's X angle.  Small numbers are more responsive.  Larger numbers give a more heavy slowly responding camera.")]
    44.         public float m_PitchDamping = 0f;
    45.  
    46.         [Range(0f, 20f)]
    47.         [Tooltip("How aggressively the camera tries to track the target rotation's Y angle.  Small numbers are more responsive.  Larger numbers give a more heavy slowly responding camera.")]
    48.         public float m_YawDamping = 0f;
    49.  
    50.         [Range(0f, 20f)]
    51.         [Tooltip("How aggressively the camera tries to track the target rotation's Z angle.  Small numbers are more responsive.  Larger numbers give a more heavy slowly responding camera.")]
    52.         public float m_RollDamping = 0f;
    53.  
    54.         public float maxSpeed = 15F;
    55.         private Vector3 velocity = Vector3.zero;
    56.         float catchDeltaTime;
    57.         CinemachineBrain brain;
    58.  
    59.         private void Start()
    60.         {
    61.             brain = Camera.main.gameObject.GetComponent<CinemachineBrain>();
    62.         }
    63.  
    64.         protected virtual void OnValidate()
    65.         {
    66.             m_FollowOffset = EffectiveOffset;
    67.         }
    68.  
    69.         /// <summary>Get the target offset, with sanitization</summary>
    70.         protected Vector3 EffectiveOffset
    71.         {
    72.             get
    73.             {
    74.                 Vector3 offset = m_FollowOffset;
    75.                 if (m_BindingMode == BindingMode.SimpleFollowWithWorldUp)
    76.                 {
    77.                     offset.x = 0;
    78.                     offset.z = -Mathf.Abs(offset.z);
    79.                 }
    80.                 return offset;
    81.             }
    82.         }
    83.  
    84.         /// <summary>True if component is enabled and has a valid Follow target</summary>
    85.         public override bool IsValid { get { return enabled && FollowTarget != null; } }
    86.  
    87.         /// <summary>Get the Cinemachine Pipeline stage that this component implements.
    88.         /// Always returns the Body stage</summary>
    89.         public override CinemachineCore.Stage Stage { get { return CinemachineCore.Stage.Body; } }
    90.  
    91.         /// <summary>Positions the virtual camera according to the transposer rules.</summary>
    92.         /// <param name="curState">The current camera state</param>
    93.         /// <param name="deltaTime">Used for damping.  If less than 0, no damping is done.</param>
    94.         public override void MutateCameraState(ref CameraState curState, float deltaTime)
    95.         {
    96.             //UnityEngine.Profiling.Profiler.BeginSample("CinemachineTransposer.MutateCameraState");
    97.             InitPrevFrameStateInfo(ref curState, deltaTime);
    98.             if (IsValid)
    99.             {
    100.                 Vector3 pos;
    101.                 Quaternion orient;
    102.                 Vector3 offset = EffectiveOffset;
    103.                 TrackTarget(deltaTime, curState.ReferenceUp, offset, out pos, out orient);
    104.                 curState.RawPosition = pos + orient * offset;
    105.                 curState.ReferenceUp = orient * Vector3.up;
    106.             }
    107.             catchDeltaTime = deltaTime;
    108.             //UnityEngine.Profiling.Profiler.EndSample();
    109.         }
    110.  
    111.         /// <summary>Initializes the state for previous frame if appropriate.</summary>
    112.         protected void InitPrevFrameStateInfo(
    113.             ref CameraState curState, float deltaTime)
    114.         {
    115.             if (m_previousTarget != FollowTarget || deltaTime < 0)
    116.             {
    117.                 m_previousTarget = FollowTarget;
    118.                 m_targetOrientationOnAssign
    119.                     = (m_previousTarget == null) ? Quaternion.identity : FollowTarget.rotation;
    120.             }
    121.             if (deltaTime < 0)
    122.             {
    123.                 m_PreviousTargetPosition = curState.RawPosition;
    124.                 m_PreviousReferenceOrientation = GetReferenceOrientation(curState.ReferenceUp);
    125.             }
    126.         }
    127.  
    128.         /// <summary>Positions the virtual camera according to the transposer rules.</summary>
    129.         /// <param name="deltaTime">Used for damping.  If less than 0, no damping is done.</param>
    130.         /// <param name="up">Current camera up</param>
    131.         /// <param name="desiredCameraOffset">Where we want to put the camera relative to the follow target</param>
    132.         /// <param name="outTargetPosition">Resulting camera position</param>
    133.         /// <param name="outTargetOrient">Damped target orientation</param>
    134.         protected void TrackTarget(
    135.             float deltaTime, Vector3 up, Vector3 desiredCameraOffset,
    136.             out Vector3 outTargetPosition, out Quaternion outTargetOrient)
    137.         {
    138.             Quaternion targetOrientation = GetReferenceOrientation(up);
    139.             Quaternion dampedOrientation = targetOrientation;
    140.             if (deltaTime >= 0)
    141.             {
    142.                 Vector3 relative = (Quaternion.Inverse(m_PreviousReferenceOrientation)
    143.                     * targetOrientation).eulerAngles;
    144.                 for (int i = 0; i < 3; ++i)
    145.                     if (relative[i] > 180)
    146.                         relative[i] -= 360;
    147.                 relative = Damper.Damp(relative, AngularDamping, deltaTime);
    148.                 dampedOrientation = m_PreviousReferenceOrientation * Quaternion.Euler(relative);
    149.             }
    150.             m_PreviousReferenceOrientation = dampedOrientation;
    151.  
    152.             Vector3 targetPosition = FollowTarget.position;
    153.             Vector3 currentPosition = m_PreviousTargetPosition;
    154.             Vector3 worldOffset = targetPosition - currentPosition;
    155.  
    156.             // Adjust for damping, which is done in camera-offset-local coords
    157.             if (deltaTime >= 0)
    158.             {
    159.                 Quaternion dampingSpace;
    160.                 if (desiredCameraOffset.AlmostZero())
    161.                     dampingSpace = VcamState.RawOrientation;
    162.                 else
    163.                     dampingSpace = Quaternion.LookRotation(dampedOrientation * desiredCameraOffset.normalized, up);
    164.                 Vector3 localOffset = Quaternion.Inverse(dampingSpace) * worldOffset;
    165.  
    166.                 localOffset = Damper.Damp(localOffset, Damping, deltaTime);
    167.  
    168.                 worldOffset = dampingSpace * localOffset;
    169.             }
    170.  
    171.             Vector3 newPos = Vector3.SmoothDamp(currentPosition, targetPosition, ref velocity, 0.1F, maxSpeed);
    172.             newPos.z = targetPosition.z;
    173.  
    174.             // if catchDeltaTime == -1 then the camera is not live
    175.             if (catchDeltaTime == -1)
    176.             {
    177.                 // Don't do this yet, because there's a 2 frame delay.
    178.                 // First the live camera changes, then 2 frames later, deltaTime is no longer -1.
    179.                 // The result is that if we do camera follow player (false), then immediately
    180.                 // move the custom camera, it will still teleport to that position.
    181.  
    182.                 // newPos = targetPosition;
    183.             }
    184.  
    185.             outTargetPosition = m_PreviousTargetPosition = newPos;
    186.  
    187.             //outTargetPosition = m_PreviousTargetPosition = currentPosition + worldOffset;
    188.             outTargetOrient = dampedOrientation;
    189.         }
    190.  
    191.         /// <summary>
    192.         /// Damping speeds for each of the 3 axes of the offset from target
    193.         /// </summary>
    194.         protected Vector3 Damping
    195.         {
    196.             get
    197.             {
    198.                 switch (m_BindingMode)
    199.                 {
    200.                     case BindingMode.SimpleFollowWithWorldUp:
    201.                         return new Vector3(0, m_YDamping, m_ZDamping);
    202.                     default:
    203.                         return new Vector3(m_XDamping, m_YDamping, m_ZDamping);
    204.                 }
    205.             }
    206.         }
    207.  
    208.         /// <summary>
    209.         /// Damping speeds for each of the 3 axes of the target's rotation
    210.         /// </summary>
    211.         protected Vector3 AngularDamping
    212.         {
    213.             get
    214.             {
    215.                 switch (m_BindingMode)
    216.                 {
    217.                     case BindingMode.LockToTargetNoRoll:
    218.                         return new Vector3(m_PitchDamping, m_YawDamping, 0);
    219.                     case BindingMode.LockToTargetWithWorldUp:
    220.                         return new Vector3(0, m_YawDamping, 0);
    221.                     case BindingMode.LockToTargetOnAssign:
    222.                     case BindingMode.WorldSpace:
    223.                     case BindingMode.SimpleFollowWithWorldUp:
    224.                         return Vector3.zero;
    225.                     default:
    226.                         return new Vector3(m_PitchDamping, m_YawDamping, m_RollDamping);
    227.                 }
    228.             }
    229.         }
    230.  
    231.         /// <summary>Internal API for the Inspector Editor, so it can draw a marker at the target</summary>
    232.         public Vector3 GeTargetCameraPosition(Vector3 worldUp)
    233.         {
    234.             if (!IsValid)
    235.                 return Vector3.zero;
    236.             return FollowTarget.position + GetReferenceOrientation(worldUp) * EffectiveOffset;
    237.         }
    238.  
    239.         /// <summary>State information for damping</summary>
    240.         Vector3 m_PreviousTargetPosition = Vector3.zero;
    241.         Quaternion m_PreviousReferenceOrientation = Quaternion.identity;
    242.         Quaternion m_targetOrientationOnAssign = Quaternion.identity;
    243.         Transform m_previousTarget = null;
    244.  
    245.         /// <summary>Internal API for the Inspector Editor, so it can draw a marker at the target</summary>
    246.         public Quaternion GetReferenceOrientation(Vector3 worldUp)
    247.         {
    248.             if (FollowTarget != null)
    249.             {
    250.                 Quaternion targetOrientation = FollowTarget.rotation;
    251.                 switch (m_BindingMode)
    252.                 {
    253.                     case BindingMode.LockToTargetOnAssign:
    254.                         return m_targetOrientationOnAssign;
    255.                     case BindingMode.LockToTargetWithWorldUp:
    256.                         return Uppify(targetOrientation, worldUp);
    257.                     case BindingMode.LockToTargetNoRoll:
    258.                         return Quaternion.LookRotation(targetOrientation * Vector3.forward, worldUp);
    259.                     case BindingMode.LockToTarget:
    260.                         return targetOrientation;
    261.                     case BindingMode.SimpleFollowWithWorldUp:
    262.                         {
    263.                             Vector3 dir = FollowTarget.position - VcamState.RawPosition;
    264.                             if (dir.AlmostZero())
    265.                                 break;
    266.                             return Uppify(Quaternion.LookRotation(dir, worldUp), worldUp);
    267.                         }
    268.                     default:
    269.                         break;
    270.                 }
    271.             }
    272.             return Quaternion.identity;
    273.         }
    274.  
    275.         static Quaternion Uppify(Quaternion q, Vector3 up)
    276.         {
    277.             Quaternion r = Quaternion.FromToRotation(q * Vector3.up, up);
    278.             return r * q;
    279.         }
    280.     }
    281. }
    282.  
     
  8. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,730
    Why don't you just use the deltaTime that's passed in as a parameter, instead of "catchDeltaTime", which will be stale by the time you look at it? That's probably the source of your delay.

    The camera speed is a function of the distance to travel and the acceleration limits that are implied by smoothTime. MaxSpeed is a speed limit - it will stay the same regardless of actual camera speed, and the camera will not necessarily reach that speed. Just like a speed limit on a highway.

    Looking at your code, I see that instead of replacing the existing Damper.Damp() calls with SmoothDamp, you have added SmoothDamp as an additional step afterwards. Seeing this, an idea has occurred to me. Why not use an ordinary out-of-the-box Transposer, and implement your SmoothDamp as a CM extension? That will eliminate the need to duplicate code and expose yourself to API changes when you upgrade CM.

    It's really quite easy to do. Something like this:
    Code (CSharp):
    1. using UnityEngine;
    2. using Cinemachine;
    3.  
    4. [AddComponentMenu("")] // Hide in menu
    5. public class CinemachineSmoothDampPosition : CinemachineExtension
    6. {
    7.     public float smoothTime = 0.1f;
    8.     public float maxSpeed = 1;
    9.  
    10.     Vector3 currentPosition;
    11.     Vector3 velocity;
    12.  
    13.     protected override void PostPipelineStageCallback(
    14.         CinemachineVirtualCameraBase vcam,
    15.         CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
    16.     {
    17.         if (stage == CinemachineCore.Stage.Body)
    18.         {
    19.             if (deltaTime < 0)
    20.                 currentPosition = state.RawPosition;
    21.             else
    22.             {
    23.                 currentPosition = Vector3.SmoothDamp(
    24.                     currentPosition, state.RawPosition, ref velocity, smoothTime, maxSpeed);
    25.                 state.RawPosition = currentPosition;
    26.             }
    27.         }
    28.     }
    29. }
    Drop it into your project and add it as an extension to the vcam via the vcam's extensions dropdown in the inspector.

    It adds SmoothDamping on top of the native damping. Turn the native damping to 0 to get pure smoothDamping. I think this solution is way better than replacing the Transposer.
     
    roseportalgames likes this.
  9. roseportalgames

    roseportalgames

    Joined:
    Aug 9, 2017
    Posts:
    173
    Wow, that works perfectly and even fixes the issue I had with maxSpeed! Thank you so much for the support and assistance!
     
    Gregoryl likes this.