using System;
using System.Collections.Generic;
using UnityEngine;
///
/// Utility class for common physics queries.
/// Eliminates repeated OverlapSphere patterns across the codebase.
///
public static class PhysicsQueryUtility
{
// Reusable buffer to avoid allocations (32 should be enough for most cases)
private static readonly Collider[] OverlapBuffer = new Collider[32];
///
/// Find the closest object implementing interface T within radius.
///
/// Interface or component type to search for
/// Center point of the search
/// Search radius
/// Layer mask to filter objects
/// Closest object of type T, or null if none found
public static T FindClosest(Vector3 origin, float radius, LayerMask layerMask) where T : class
{
int count = Physics.OverlapSphereNonAlloc(origin, radius, OverlapBuffer, layerMask);
T closest = null;
float minDistSqr = float.MaxValue;
for (int i = 0; i < count; i++)
{
T component = OverlapBuffer[i].GetComponentInParent();
if (component != null)
{
float distSqr = (origin - OverlapBuffer[i].transform.position).sqrMagnitude;
if (distSqr < minDistSqr)
{
minDistSqr = distSqr;
closest = component;
}
}
}
return closest;
}
///
/// Find the closest object implementing interface T within radius, also returning distance.
///
public static T FindClosest(Vector3 origin, float radius, LayerMask layerMask, out float distance) where T : class
{
int count = Physics.OverlapSphereNonAlloc(origin, radius, OverlapBuffer, layerMask);
T closest = null;
float minDistSqr = float.MaxValue;
for (int i = 0; i < count; i++)
{
T component = OverlapBuffer[i].GetComponentInParent();
if (component != null)
{
float distSqr = (origin - OverlapBuffer[i].transform.position).sqrMagnitude;
if (distSqr < minDistSqr)
{
minDistSqr = distSqr;
closest = component;
}
}
}
distance = closest != null ? Mathf.Sqrt(minDistSqr) : 0f;
return closest;
}
///
/// Execute action on all objects of type T within radius.
///
/// Interface or component type to search for
/// Center point of the search
/// Search radius
/// Layer mask to filter objects
/// Action to execute on each found object
public static void ForEachInRadius(Vector3 origin, float radius, LayerMask layerMask, Action action) where T : class
{
int count = Physics.OverlapSphereNonAlloc(origin, radius, OverlapBuffer, layerMask);
// Track processed objects to avoid duplicates (same object hit by multiple colliders)
var processed = new HashSet();
for (int i = 0; i < count; i++)
{
T component = OverlapBuffer[i].GetComponentInParent();
if (component != null && !processed.Contains(component))
{
processed.Add(component);
action(component);
}
}
}
///
/// Execute action on all objects of type T within radius, with additional context.
///
public static void ForEachInRadius(Vector3 origin, float radius, LayerMask layerMask,
Action action) where T : class
{
int count = Physics.OverlapSphereNonAlloc(origin, radius, OverlapBuffer, layerMask);
var processed = new HashSet();
for (int i = 0; i < count; i++)
{
T component = OverlapBuffer[i].GetComponentInParent();
if (component != null && !processed.Contains(component))
{
processed.Add(component);
action(component, OverlapBuffer[i]);
}
}
}
///
/// Get all objects of type T within radius.
///
/// Interface or component type to search for
/// Center point of the search
/// Search radius
/// Layer mask to filter objects
/// List of all objects of type T within radius
public static List FindAllInRadius(Vector3 origin, float radius, LayerMask layerMask) where T : class
{
var results = new List();
int count = Physics.OverlapSphereNonAlloc(origin, radius, OverlapBuffer, layerMask);
for (int i = 0; i < count; i++)
{
T component = OverlapBuffer[i].GetComponentInParent();
if (component != null && !results.Contains(component))
{
results.Add(component);
}
}
return results;
}
///
/// Find all objects of type T within radius, sorted by distance (closest first).
///
public static List FindAllInRadiusSorted(Vector3 origin, float radius, LayerMask layerMask) where T : class
{
int count = Physics.OverlapSphereNonAlloc(origin, radius, OverlapBuffer, layerMask);
var resultsWithDistance = new List<(T component, float distSqr)>();
for (int i = 0; i < count; i++)
{
T component = OverlapBuffer[i].GetComponentInParent();
if (component != null)
{
bool alreadyAdded = false;
foreach (var item in resultsWithDistance)
{
if (ReferenceEquals(item.component, component))
{
alreadyAdded = true;
break;
}
}
if (!alreadyAdded)
{
float distSqr = (origin - OverlapBuffer[i].transform.position).sqrMagnitude;
resultsWithDistance.Add((component, distSqr));
}
}
}
// Sort by distance
resultsWithDistance.Sort((a, b) => a.distSqr.CompareTo(b.distSqr));
var results = new List(resultsWithDistance.Count);
foreach (var item in resultsWithDistance)
{
results.Add(item.component);
}
return results;
}
///
/// Check if any object of type T exists within radius.
///
public static bool AnyInRadius(Vector3 origin, float radius, LayerMask layerMask) where T : class
{
int count = Physics.OverlapSphereNonAlloc(origin, radius, OverlapBuffer, layerMask);
for (int i = 0; i < count; i++)
{
if (OverlapBuffer[i].GetComponentInParent() != null)
{
return true;
}
}
return false;
}
///
/// Count objects of type T within radius.
///
public static int CountInRadius(Vector3 origin, float radius, LayerMask layerMask) where T : class
{
int count = Physics.OverlapSphereNonAlloc(origin, radius, OverlapBuffer, layerMask);
var processed = new HashSet();
for (int i = 0; i < count; i++)
{
T component = OverlapBuffer[i].GetComponentInParent();
if (component != null)
{
processed.Add(component);
}
}
return processed.Count;
}
}