229 lines
7.8 KiB
C#
229 lines
7.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// Utility class for common physics queries.
|
|
/// Eliminates repeated OverlapSphere patterns across the codebase.
|
|
/// </summary>
|
|
public static class PhysicsQueryUtility
|
|
{
|
|
// Reusable buffer to avoid allocations (32 should be enough for most cases)
|
|
private static readonly Collider[] OverlapBuffer = new Collider[32];
|
|
|
|
/// <summary>
|
|
/// Find the closest object implementing interface T within radius.
|
|
/// </summary>
|
|
/// <typeparam name="T">Interface or component type to search for</typeparam>
|
|
/// <param name="origin">Center point of the search</param>
|
|
/// <param name="radius">Search radius</param>
|
|
/// <param name="layerMask">Layer mask to filter objects</param>
|
|
/// <returns>Closest object of type T, or null if none found</returns>
|
|
public static T FindClosest<T>(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<T>();
|
|
if (component != null)
|
|
{
|
|
float distSqr = (origin - OverlapBuffer[i].transform.position).sqrMagnitude;
|
|
if (distSqr < minDistSqr)
|
|
{
|
|
minDistSqr = distSqr;
|
|
closest = component;
|
|
}
|
|
}
|
|
}
|
|
|
|
return closest;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find the closest object implementing interface T within radius, also returning distance.
|
|
/// </summary>
|
|
public static T FindClosest<T>(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<T>();
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute action on all objects of type T within radius.
|
|
/// </summary>
|
|
/// <typeparam name="T">Interface or component type to search for</typeparam>
|
|
/// <param name="origin">Center point of the search</param>
|
|
/// <param name="radius">Search radius</param>
|
|
/// <param name="layerMask">Layer mask to filter objects</param>
|
|
/// <param name="action">Action to execute on each found object</param>
|
|
public static void ForEachInRadius<T>(Vector3 origin, float radius, LayerMask layerMask, Action<T> 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<T>();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
T component = OverlapBuffer[i].GetComponentInParent<T>();
|
|
if (component != null && !processed.Contains(component))
|
|
{
|
|
processed.Add(component);
|
|
action(component);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute action on all objects of type T within radius, with additional context.
|
|
/// </summary>
|
|
public static void ForEachInRadius<T>(Vector3 origin, float radius, LayerMask layerMask,
|
|
Action<T, Collider> action) where T : class
|
|
{
|
|
int count = Physics.OverlapSphereNonAlloc(origin, radius, OverlapBuffer, layerMask);
|
|
|
|
var processed = new HashSet<T>();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
T component = OverlapBuffer[i].GetComponentInParent<T>();
|
|
if (component != null && !processed.Contains(component))
|
|
{
|
|
processed.Add(component);
|
|
action(component, OverlapBuffer[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all objects of type T within radius.
|
|
/// </summary>
|
|
/// <typeparam name="T">Interface or component type to search for</typeparam>
|
|
/// <param name="origin">Center point of the search</param>
|
|
/// <param name="radius">Search radius</param>
|
|
/// <param name="layerMask">Layer mask to filter objects</param>
|
|
/// <returns>List of all objects of type T within radius</returns>
|
|
public static List<T> FindAllInRadius<T>(Vector3 origin, float radius, LayerMask layerMask) where T : class
|
|
{
|
|
var results = new List<T>();
|
|
int count = Physics.OverlapSphereNonAlloc(origin, radius, OverlapBuffer, layerMask);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
T component = OverlapBuffer[i].GetComponentInParent<T>();
|
|
if (component != null && !results.Contains(component))
|
|
{
|
|
results.Add(component);
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find all objects of type T within radius, sorted by distance (closest first).
|
|
/// </summary>
|
|
public static List<T> FindAllInRadiusSorted<T>(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<T>();
|
|
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<T>(resultsWithDistance.Count);
|
|
foreach (var item in resultsWithDistance)
|
|
{
|
|
results.Add(item.component);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if any object of type T exists within radius.
|
|
/// </summary>
|
|
public static bool AnyInRadius<T>(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<T>() != null)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Count objects of type T within radius.
|
|
/// </summary>
|
|
public static int CountInRadius<T>(Vector3 origin, float radius, LayerMask layerMask) where T : class
|
|
{
|
|
int count = Physics.OverlapSphereNonAlloc(origin, radius, OverlapBuffer, layerMask);
|
|
|
|
var processed = new HashSet<T>();
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
T component = OverlapBuffer[i].GetComponentInParent<T>();
|
|
if (component != null)
|
|
{
|
|
processed.Add(component);
|
|
}
|
|
}
|
|
|
|
return processed.Count;
|
|
}
|
|
}
|