Search Unity

Custom Cinemachine Collider strategy

Discussion in 'Cinemachine' started by Roman-Ilyin, Dec 17, 2020.

  1. Roman-Ilyin

    Roman-Ilyin

    Joined:
    Oct 9, 2013
    Posts:
    29
    Hello.
    I would like to add custom Cinemachine Collider strategy (Freelook rig):
    First, increase the angle of the camera until it looks from above (1 -> 2 -> 3).
    Then use Pull Camera Forward (3 -> 4).

    Can you please tell me how to write an CinemachineExtension to add a new strategy for the collider?
     
    Last edited: Dec 17, 2020
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,724
    It won't be so easy to add a new strategy to the existing collider, but you can write a new collider extension that behaves the way you want. The idea behind the extension is that at each stage of the Cinemachine pipeline the extension can post-process the camera state.

    The CM pipeline is
    1. Body: camera is positioned
    2. Aim: camera is rotated
    3. Noise: corrections are applied
    4. Finalize: (nothing happens here by default, but it is a placeholder)​

    The extension implements the PostPipelineStageCallback() method, which receives a pointer to the vcam and its current state at this point in the pipeline. It gets called multiple times per frame, once at the end of each of the pipeline stages. It can modify the CameraState object, which contains current camera position, rotation, lens, and other stuff that will feed the next pipeline stage and ultimately get pushed to the main camera when the vcam is active.

    The best way to proceed is to start with an existing extension, copy it to get the basic structure, and start modifying. You can use CinemachineCollider.cs for this, although it is one of the more complex extensions and might be a bit bewildering. CinemachineFollowZoom.cs might be a better starting point.
     
    Roman-Ilyin likes this.
  3. JoshOClock

    JoshOClock

    Joined:
    Dec 8, 2010
    Posts:
    107
    I broke down the cinemachine collider into what I see most games do for a 3rd person camera. Simplest implementation I could do. Leaving it here for posterity before I mess it up too much.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. namespace Cinemachine
    4. {
    5.     /// <summary>
    6.     /// </summary>
    7.     [AddComponentMenu("")] // Hide in menu
    8.     [SaveDuringPlay]
    9.     [ExecuteAlways]
    10.     [DisallowMultipleComponent]
    11.     public class CinemachineSimpleCollider : CinemachineExtension
    12.     {
    13.         /// <summary>Objects on these layers will be detected.</summary>
    14.         [Header("Obstacle Detection")]
    15.         [Tooltip("Objects on these layers will be detected")]
    16.         public LayerMask m_CollideAgainst = 1;
    17.  
    18.         /// <summary>Obstacles with this tag will be ignored.  It is a good idea to set this field to the target's tag</summary>
    19.         [TagField]
    20.         [Tooltip("Obstacles with this tag will be ignored.  It is a good idea to set this field to the target's tag")]
    21.         public string m_IgnoreTag = string.Empty;
    22.  
    23.         [Tooltip("How fast to return to distance after collision, higher is faster")]
    24.         [Range(1, 100)]
    25.         public float m_ReturnAfterCollisionRate = 5f;
    26.      
    27.         /// <summary>Objects on these layers will never obstruct view of the target.</summary>
    28.         [Tooltip("Objects on these layers will never obstruct view of the target")]
    29.         public LayerMask m_TransparentLayers = 0;
    30.      
    31.         public bool FoundCollision(ICinemachineCamera vcam)
    32.         {
    33.             var extra = GetExtraState<VcamExtraState>(vcam);
    34.             return (Time.time - extra.lastCollideTime < 0.1f);
    35.         }
    36.      
    37.         void OnValidate()
    38.         {
    39.         }
    40.      
    41.         /// <summary>
    42.         /// Per-vcam extra state info
    43.         /// </summary>
    44.         class VcamExtraState
    45.         {
    46.             public float lastCollideTime;
    47.             public float currentReturnRate;
    48.             public float previousAdjustment = 5f;
    49.         }
    50.      
    51.         RaycastHit[] m_RaycastBuffer = new RaycastHit[4];
    52.  
    53.         /// <summary>Callback to preform the zoom adjustment</summary>
    54.         /// <param name="vcam">The virtual camera being processed</param>
    55.         /// <param name="stage">The current pipeline stage</param>
    56.         /// <param name="state">The current virtual camera state</param>
    57.         /// <param name="deltaTime">The current applicable deltaTime</param>
    58.         protected override void PostPipelineStageCallback(
    59.             CinemachineVirtualCameraBase vcam,
    60.             CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
    61.         {
    62.             var extra = GetExtraState<VcamExtraState>(vcam);
    63.          
    64.             // Set the zoom after the body has been positioned, but before the aim,
    65.             // so that composer can compose using the updated fov.
    66.             if (stage == CinemachineCore.Stage.Body)
    67.             {
    68.                 Vector3 cameraPos = state.CorrectedPosition;
    69.                 Vector3 lookAtPos = state.ReferenceLookAt;
    70.  
    71.                 int layerMask = m_CollideAgainst & ~m_TransparentLayers;
    72.              
    73.                 Vector3 displacement = Vector3.zero;
    74.                 Vector3 targetToCamera = cameraPos - lookAtPos;
    75.                 Ray targetToCameraRay = new Ray(lookAtPos, targetToCamera);
    76.                 Vector3 targetToCameraNormalized = targetToCamera.normalized;
    77.              
    78.                 float distToCamera = targetToCamera.magnitude;
    79.                 if (distToCamera > Epsilon)
    80.                 {
    81.                     float desiredDistToCamera = distToCamera;
    82.                  
    83.                     int numFound = Physics.SphereCastNonAlloc(
    84.                         lookAtPos, 0.1f, targetToCameraNormalized, m_RaycastBuffer, distToCamera + 1f, // Trace back a bit, in case we 'will' collide next frame
    85.                         layerMask, QueryTriggerInteraction.Ignore);
    86.                     if (numFound > 0)
    87.                     {
    88.                         float bestDist = distToCamera;
    89.                         for (int i = 0; i < numFound; ++i)
    90.                         {
    91.                             var castHitInfo = m_RaycastBuffer[i];
    92.                             Vector3 castPoint = castHitInfo.point;//Vector3.Project(castHitInfo.point, targetToCameraNormalized);
    93.                             float dist = Vector3.Distance(lookAtPos, castPoint);
    94.                             if (dist < bestDist)
    95.                             {
    96.                                 bestDist = dist;
    97.                             }
    98.                         }
    99.  
    100.                         if (bestDist < distToCamera)
    101.                         {
    102.                             extra.lastCollideTime = Time.time;
    103.                             extra.currentReturnRate = 0f;
    104.                             desiredDistToCamera = bestDist;
    105.                         }
    106.                     }
    107.                  
    108.                     if (extra.previousAdjustment > desiredDistToCamera)
    109.                     {
    110.                         extra.previousAdjustment = desiredDistToCamera;
    111.                     }
    112.                     else
    113.                     {
    114.                         // Add an extra lerp up to full value to make it buttery
    115.                         extra.currentReturnRate = Mathf.Lerp(extra.currentReturnRate, m_ReturnAfterCollisionRate, 5f * Time.deltaTime);
    116.                         extra.previousAdjustment = Mathf.Lerp(extra.previousAdjustment, desiredDistToCamera, extra.currentReturnRate * Time.deltaTime);
    117.                     }
    118.                  
    119.                     displacement = targetToCameraRay.GetPoint(extra.previousAdjustment) - cameraPos;
    120.                 }
    121.  
    122.                 state.PositionCorrection += displacement;
    123.             }
    124.         }
    125.     }
    126. }
     
    claudius_I, icyuan, jrsvd and 3 others like this.
  4. nehvaleem

    nehvaleem

    Joined:
    Dec 13, 2012
    Posts:
    437
    I know that it's been a while, but I just found that I need something exactly like this and started to search for existing topics before I would write a new one. Have you managed to get to pull that off?
     
    raniaRMR likes this.