Created
October 24, 2025 14:44
-
-
Save AdamAtomic/e198dd3d2cc09ed8fd64990f07f23328 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using UnityEngine; | |
| using System; | |
| namespace Cinemachine | |
| { | |
| [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)] | |
| [SaveDuringPlay] | |
| [ExecuteAlways] | |
| [DisallowMultipleComponent] | |
| public class CinemachineBetterCollider : CinemachineExtension | |
| { | |
| [Tooltip( "Starting offset of obstruction check (bigger value helps with bigger radii and closer surfaces)" )] | |
| public float m_CastOffset = 0.1f; | |
| [Tooltip( "Radius of obstruction check (bigger value helps with tight corners / cutaway issues)" )] | |
| public float m_CastRadius = 0.1f; | |
| [Tooltip( "Objects on these layers will be detected" )] | |
| public LayerMask m_CastAgainst = 1; | |
| [Tooltip( "Forced / snapped minimum distance camera can be from the back of the spherecast (and usually the colliding surface)" )] | |
| public float m_MinimumBuffer = 0.05f; | |
| [Tooltip( "Never go closer than this" )] | |
| public float m_MinimumDistance = 0.2f; | |
| [Tooltip( "Alt minimum distance (to support two way smoothing) from the back of the spherecast (and usually the colliding surface)" )] | |
| public float m_PreferredBuffer = 0.15f; | |
| [Tooltip( "How fast to smooth toward the preferred buffer each frame (in units per second)" )] | |
| public float m_SmoothingSpeed = 10.0f; | |
| private void OnValidate() | |
| { | |
| m_CastOffset = Mathf.Max( 0, m_CastOffset ); | |
| m_CastRadius = Mathf.Max( 0, m_CastRadius ); | |
| m_MinimumBuffer = Mathf.Max( 0, m_MinimumBuffer ); | |
| m_MinimumDistance = Mathf.Max( 0, m_MinimumDistance ); | |
| m_PreferredBuffer = Mathf.Max( m_MinimumBuffer, m_PreferredBuffer ); | |
| m_SmoothingSpeed = Mathf.Max( 0.1f, m_SmoothingSpeed ); | |
| } | |
| class VcamExtraState | |
| { | |
| public float previousDistance; | |
| } | |
| RaycastHit[] hits = new RaycastHit[4]; | |
| protected override void PostPipelineStageCallback( | |
| CinemachineVirtualCameraBase vcam, | |
| CinemachineCore.Stage stage, ref CameraState state, float deltaTime) | |
| { | |
| if (stage == CinemachineCore.Stage.Body) | |
| { | |
| var eulerX = state.CorrectedOrientation.eulerAngles.x; | |
| if( eulerX > 180.0f ) { eulerX -= 360.0f; } | |
| var overhead = Mathf.InverseLerp( 0, 90, Mathf.Abs( eulerX ) ); | |
| var lookAt = state.ReferenceLookAt; | |
| var cameraPos = state.CorrectedPosition; | |
| var cameraRadius = ( 1.0f - overhead ) * ( 1.0f - overhead ) * m_CastRadius; | |
| var direction = Vector3.Normalize( cameraPos - lookAt ); | |
| var origin = lookAt + direction * (m_CastOffset + cameraRadius); | |
| var distance = Vector3.Distance( origin, cameraPos ) - cameraRadius + m_PreferredBuffer; | |
| var castCenter = origin + direction * distance; | |
| var castPrefPos = castCenter + direction * (cameraRadius - m_PreferredBuffer); | |
| var castPrefDist = Vector3.Distance( lookAt, castPrefPos ); | |
| Array.Clear( hits, 0, hits.Length ); | |
| var hitCount = Physics.SphereCastNonAlloc( origin, cameraRadius, direction, hits, distance, m_CastAgainst ); | |
| if( hitCount > 0 ) | |
| { | |
| var hit = hits[0]; | |
| for( int i = 0; i < hitCount; i++ ) | |
| { | |
| if( hits[i].distance < hit.distance ) | |
| { | |
| hit = hits[i]; | |
| } | |
| } | |
| castCenter = origin + direction * hit.distance; | |
| castPrefPos = castCenter + direction * (cameraRadius - m_PreferredBuffer); | |
| castPrefDist = Vector3.Distance( lookAt, castPrefPos ); | |
| } | |
| VcamExtraState extra = GetExtraState<VcamExtraState>( vcam ); | |
| if( deltaTime < 0 || !VirtualCamera.PreviousStateIsValid ) | |
| { | |
| extra.previousDistance = castPrefDist; | |
| } | |
| if( extra.previousDistance < castPrefDist ) | |
| { | |
| castPrefPos = lookAt + direction * Mathf.Min( extra.previousDistance + m_SmoothingSpeed * Time.deltaTime, castPrefDist ); | |
| } | |
| else if( extra.previousDistance > castPrefDist ) | |
| { | |
| castPrefPos = lookAt + direction * Mathf.Max( extra.previousDistance - m_SmoothingSpeed * Time.deltaTime, castPrefDist ); | |
| } | |
| var castSafePos = castCenter + direction * (cameraRadius - m_MinimumBuffer); | |
| if( Vector3.Distance( lookAt, castPrefPos ) > Vector3.Distance( lookAt, castSafePos ) ) | |
| { | |
| castPrefPos = castSafePos; | |
| } | |
| if( Vector3.Distance( lookAt, castPrefPos ) <= m_MinimumDistance ) | |
| { | |
| castPrefPos = lookAt + direction * m_MinimumDistance; | |
| } | |
| state.PositionCorrection += castPrefPos - cameraPos; | |
| extra.previousDistance = Vector3.Distance( lookAt, state.CorrectedPosition ); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment