Search Unity

Question Writing custom components for Cinemachine 3

Discussion in 'Cinemachine' started by mishakozlov74, Apr 14, 2023.

  1. mishakozlov74

    mishakozlov74

    Joined:
    Aug 8, 2018
    Posts:
    143
    Hey, I'm trying to move some code from the old project and want to use the new version of the package.
    I wrote custom body and aim scripts and now trying to rewrite them from v2 to v3.

    I tried to read source code of components, and I noticed that some of them (CInemachineFollow) use variable Tracker m_TargetTracker; First of all, according to the documentation we don't use prefix m_ in names, but it's okay.

    Could somebody please explain to me the purpose of the variable? It's extensively used in the component, but it's internal and I can't use it in mine.

    Thanks.
     
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    We still use m_ for private variables. The ban only applies to public API. You can look at the code, even though it's internal. It implements the tracking of the follow target, with position and rotation damping.

    Can I ask what your custom body and aim scripts do?
     
  3. mishakozlov74

    mishakozlov74

    Joined:
    Aug 8, 2018
    Posts:
    143
    Thanks for the explanation, I couldn't open it in Rider, it was saying that it couldn't find it's definition... Anyway.

    My scripts are relatively simple, it's for for following space ship in a very specific manner.

    Body just follows the target with damping

    Code (CSharp):
    1. public class ShipBody : CinemachineComponentBase {
    2.  
    3.         [Tooltip("How much time it takes for the position to catch up to the target's position")]
    4.         [Range(0, 1)]
    5.         public float m_Damping = 0;
    6.         Vector3 m_PreviousTargetPosition;
    7.  
    8.         [Tooltip("How far camera should be from the player")]
    9.         public Vector3 m_PositionOffset;
    10.  
    11.         public Transform m_LookAheadRig;
    12.    
    13.         public override bool IsValid => enabled && FollowTarget != null;
    14.         public override CinemachineCore.Stage Stage => CinemachineCore.Stage.Body;
    15.  
    16.         public override float GetMaxDampTime() { return m_Damping; }
    17.  
    18.         public override void MutateCameraState(ref CameraState curState, float deltaTime) {
    19.             if (!IsValid)
    20.                 return;
    21.  
    22.             // Target position
    23.             Vector3 dampedPos = FollowTargetPosition;
    24.  
    25.             if (deltaTime >= 0) {
    26.  
    27.                 // Previous + slightly closer to the target
    28.                 dampedPos = m_PreviousTargetPosition + VirtualCamera.DetachedFollowTargetDamp(
    29.                     dampedPos - m_PreviousTargetPosition,
    30.                     m_Damping,
    31.                     deltaTime);
    32.             }
    33.  
    34.             m_PreviousTargetPosition = dampedPos;
    35.             curState.RawPosition = dampedPos;
    36.             curState.PositionCorrection = m_LookAheadRig.rotation * m_PositionOffset;
    37.         }
    38.    
    39.         public override void OnTargetObjectWarped(Transform target, Vector3 positionDelta)
    40.         {
    41.             base.OnTargetObjectWarped(target, positionDelta);
    42.             if (target == FollowTarget)
    43.                 m_PreviousTargetPosition = positionDelta;
    44.         }
    45.  
    46.     }

    And aim is little more complicated, it uses mouse position to calculate orientation using additional rig transform

    Code (CSharp):
    1. public class ShipAim : CinemachineComponentBase {
    2.  
    3.         [Tooltip("How much time it takes for the position to catch up to the target's position")]
    4.         [Range(0, 1)]
    5.         public float m_Damping = 0;
    6.  
    7.         [Range(0, 1)]
    8.         public float m_RigRotationDamping = 0;
    9.  
    10.         [Range(0, 1)]
    11.         public float m_Power = 1;
    12.  
    13.         public bool m_UseTargetUp = false;
    14.  
    15.         Quaternion m_PreviousReferenceOrientation = Quaternion.identity;
    16.  
    17.         Quaternion m_PreviousLookAheadQuaternion = Quaternion.identity;
    18.  
    19.         private Vector3 m_PreviousUpVector;
    20.  
    21.         public Transform m_LookAheadRig;
    22.  
    23.         private Vector2 m_lastMousePosition;
    24.  
    25.         [Header("Lookahead Values")]
    26.         [SerializeField] private float m_HorizontalTurnAngle = 15f;
    27.         [SerializeField] private float m_VerticalTurnUpAngle = 5.0f;
    28.         [SerializeField] private float m_VerticalTurnDownAngle = 15.0f;
    29.    
    30.         [Inject]
    31.         public InputControls m_Controls;
    32.         private InputAction m_EngineInputAction;
    33.  
    34.         public override float GetMaxDampTime() { return m_Damping; }
    35.  
    36.         private void OnEnable() {
    37.             m_EngineInputAction = m_Controls.Ship.Look;
    38.             m_EngineInputAction.performed += EngineInputActionOnperformed;
    39.             m_EngineInputAction.Enable();
    40.  
    41.             m_PreviousUpVector = m_UseTargetUp ? Vector3.up : VirtualCamera.transform.up;
    42.         }
    43.  
    44.         private void OnDisable() {
    45.             m_EngineInputAction.performed -= EngineInputActionOnperformed;
    46.         }
    47.  
    48.         private void EngineInputActionOnperformed(InputAction.CallbackContext obj) {
    49.             m_lastMousePosition = m_EngineInputAction.ReadValue<Vector2>();
    50.         }
    51.  
    52.         public override bool IsValid => enabled && FollowTarget != null;
    53.         public override CinemachineCore.Stage Stage => CinemachineCore.Stage.Aim;
    54.  
    55.         public override void MutateCameraState(ref CameraState curState, float deltaTime) {
    56.  
    57.             if (!IsValid)
    58.                 return;
    59.  
    60.             // Target up vector
    61.             Vector3 dampedVectorUp = m_UseTargetUp ? FollowTarget.up : VirtualCamera.transform.up;
    62.        
    63.             // Damped target up vector
    64.             dampedVectorUp = m_PreviousUpVector + VirtualCamera.DetachedFollowTargetDamp(
    65.                 dampedVectorUp - m_PreviousUpVector,
    66.                 m_Damping,
    67.                 deltaTime);
    68.        
    69.  
    70.             // Look at target
    71.             Quaternion dampedOrientation = Quaternion.LookRotation(
    72.                 FollowTarget.forward,
    73.                 dampedVectorUp
    74.             );
    75.  
    76.             // If not first frame, damp
    77.             if (deltaTime >= 0) {
    78.                 var t = VirtualCamera.DetachedFollowTargetDamp(1, m_Damping, deltaTime);
    79.                 dampedOrientation = Quaternion.Slerp(
    80.                     m_PreviousReferenceOrientation, dampedOrientation, t);
    81.             }
    82.        
    83.             // Update previous values
    84.             m_PreviousReferenceOrientation = dampedOrientation;
    85.             m_PreviousUpVector = dampedVectorUp;
    86.        
    87.             // Update state
    88.             curState.RawOrientation = dampedOrientation;
    89.             curState.OrientationCorrection = RotateRigAndCameraForLookahead(deltaTime) * Quaternion.Inverse(dampedOrientation);
    90.         }
    91.  
    92.         public override void ForceCameraPosition(Vector3 pos, Quaternion rot) {
    93.             m_PreviousReferenceOrientation = rot;
    94.             VirtualCamera.transform.rotation = rot;
    95.             m_PreviousUpVector = FollowTarget.up;
    96.         }
    97.    
    98.         private Quaternion RotateRigAndCameraForLookahead(float deltaTime) {
    99.             // Use the mouse position to figure out where on the screen the mouse is. When the
    100.             // mouse is at extremes, the camera will turn to look in that direction, giving the
    101.             // appearance of the ship swinging around in a really nice way.
    102.        
    103.             // Normalize screen positions so that the range is -1 to 1. Makes the math easier.
    104.             var mouseScreenX = m_lastMousePosition.x;
    105.             var mouseScreenY = -m_lastMousePosition.y;
    106.        
    107.             // Use the horizontal and vertical turn angles to rotate the camera's local rig position.
    108.             float horizontal = 0f;
    109.             float vertical = 0f;
    110.             horizontal = m_HorizontalTurnAngle * mouseScreenX;
    111.             vertical = (mouseScreenY < 0.0f) ? m_VerticalTurnUpAngle * mouseScreenY : m_VerticalTurnDownAngle * mouseScreenY;
    112.  
    113.             // Rotate only the lookahead rig. The lookahead rig is a separate transform under the
    114.             // the regular camera rig that is used only for this lookahead motion. This allows there
    115.             // to easily be different "channels" of camera motion that all function independently of
    116.             // each other.
    117.  
    118.             Quaternion dampedOrientation = Quaternion.Slerp(
    119.                 Quaternion.identity,
    120.                 Quaternion.Euler(-vertical, -horizontal, 0f),
    121.                 m_Power);
    122.  
    123.             if (deltaTime >= 0) {
    124.                 float t = VirtualCamera.DetachedFollowTargetDamp(1, m_RigRotationDamping, deltaTime);
    125.                 dampedOrientation = Quaternion.Slerp(
    126.                     m_PreviousLookAheadQuaternion, dampedOrientation, t);
    127.             }
    128.             m_PreviousLookAheadQuaternion = dampedOrientation;
    129.             m_LookAheadRig.localRotation = dampedOrientation;
    130.  
    131.             // After rotating the look rig, the camera needs to continue pointing forwards.
    132.             // Point the camera at some point projected forwards from the ship.
    133.             Vector3 lookaheadPosition = FollowTarget.transform.TransformPoint(Vector3.forward * 100f);
    134.  
    135.             return Quaternion.LookRotation(lookaheadPosition - m_LookAheadRig.position, m_LookAheadRig.up);
    136.  
    137.         }
    138.     }
     
  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    Very nice! It looks great in your video. Your components should work fine in CM3, if you make a couple of small changes:
    • In CinemachineComponentBase, OnEnable and OnDisable are virtual, so you should be overriding and calling the base class implementations.
    • You need to add the class attribute [CameraPipeline(CinemachineCore.Stage.Body)] on the Body component and [CameraPipeline(CinemachineCore.Stage.Aim)] on the Aim component. This is for the inspector menus to work.
    You don't need to use the Tracker class, since your component implements that functionality on its own.
     
    Yuchen_Chang and mishakozlov74 like this.
  5. mishakozlov74

    mishakozlov74

    Joined:
    Aug 8, 2018
    Posts:
    143
    Okay, got it!
    Thanks a lot!