Skip to content

Instantly share code, notes, and snippets.

@AdamAtomic
Created October 24, 2025 14:44
Show Gist options
  • Select an option

  • Save AdamAtomic/e198dd3d2cc09ed8fd64990f07f23328 to your computer and use it in GitHub Desktop.

Select an option

Save AdamAtomic/e198dd3d2cc09ed8fd64990f07f23328 to your computer and use it in GitHub Desktop.
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