public static class SpotLightCastExtension
{
public static bool InLineOfSight(this UnityEngine.Light light, Vector3 targetWorldPosition, System.Predicate<RaycastHit> collisionTest = null)
{
Transform lightTransform = light.transform;
return ConeCastTest(lightTransform.position, lightTransform.forward, light.spotAngle, light.range, targetWorldPosition, collisionTest);
}
public static bool ConeCastTest(Vector3 sourcePosition, Vector3 forward, float openingAngle, float range, Vector3 testPosition, System.Predicate<RaycastHit> validHit = null)
{
Vector3 dirDetector = (testPosition - sourcePosition).normalized;
bool visible = Vector3.Distance(testPosition, sourcePosition) <= range;
Vector3 start = sourcePosition + forward * range;
Vector3 projectedDetectedPositionEnd = sourcePosition + dirDetector * range;
float detectorConeDistance = Vector3.Distance(start, projectedDetectedPositionEnd);
// if projected object position towards center of light both at end
// is further distanced than the openAngle max distance
if (detectorConeDistance > 4 * openingAngle * Mathf.Deg2Rad)
{
visible = false;
}
// Cast light towards detector, is it blocked by a collider?
else if (visible
&& Physics.Linecast(sourcePosition, projectedDetectedPositionEnd, out RaycastHit hit)
&& validHit is not null
&& !validHit.Invoke(hit))
{
visible = false;
}
return visible;
}
}