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; } }