90 lines
3.3 KiB
C#
90 lines
3.3 KiB
C#
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
|
|
/// <summary>
|
|
/// EntityRegistry — Zero-allocation replacement for FindObjectsOfType.
|
|
/// Each MonoBehaviour type auto-registers in OnEnable/OnDisable via EntityRegistryMember<T>.
|
|
/// Access all instances via EntityRegistry<T>.All (read-only list, no allocation).
|
|
///
|
|
/// Usage:
|
|
/// 1) Add EntityRegistryMember<CashBag> component, or
|
|
/// 2) Call EntityRegistry<CashBag>.Register(this) in OnEnable / Unregister in OnDisable
|
|
/// 3) Query: foreach (var bag in EntityRegistry<CashBag>.All) { ... }
|
|
/// </summary>
|
|
public static class EntityRegistry<T> where T : class
|
|
{
|
|
private static readonly List<T> _instances = new List<T>(32);
|
|
private static readonly List<T> _readOnly = _instances; // Same ref, cast prevents mutation at call sites
|
|
|
|
/// <summary>All currently active instances. Do NOT modify this list.</summary>
|
|
public static List<T> All => _readOnly;
|
|
|
|
/// <summary>Alias for All — convenience method matching GetAll() convention.</summary>
|
|
public static List<T> GetAll() => _readOnly;
|
|
|
|
/// <summary>Number of active instances.</summary>
|
|
public static int Count => _instances.Count;
|
|
|
|
public static void Register(T instance)
|
|
{
|
|
if (instance != null && !_instances.Contains(instance))
|
|
_instances.Add(instance);
|
|
}
|
|
|
|
public static void Unregister(T instance)
|
|
{
|
|
_instances.Remove(instance);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find nearest instance to a world position.
|
|
/// Returns null if no instances. O(n) but n is small (≤10 for bags/characters).
|
|
/// </summary>
|
|
public static T FindNearest(Vector3 position, System.Func<T, Vector3> getPosition, System.Func<T, bool> filter = null)
|
|
{
|
|
T nearest = null;
|
|
float nearestSqrDist = float.MaxValue;
|
|
|
|
for (int i = 0; i < _instances.Count; i++)
|
|
{
|
|
T instance = _instances[i];
|
|
if (instance == null) continue;
|
|
if (filter != null && !filter(instance)) continue;
|
|
|
|
float sqrDist = (getPosition(instance) - position).sqrMagnitude;
|
|
if (sqrDist < nearestSqrDist)
|
|
{
|
|
nearestSqrDist = sqrDist;
|
|
nearest = instance;
|
|
}
|
|
}
|
|
return nearest;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find all instances within radius. Uses pre-allocated result list to avoid allocation.
|
|
/// Caller should process results immediately (list is reused).
|
|
/// </summary>
|
|
private static readonly List<T> _withinRadiusResults = new List<T>(16);
|
|
public static List<T> FindWithinRadius(Vector3 position, float radius, System.Func<T, Vector3> getPosition, System.Func<T, bool> filter = null)
|
|
{
|
|
_withinRadiusResults.Clear();
|
|
float sqrRadius = radius * radius;
|
|
|
|
for (int i = 0; i < _instances.Count; i++)
|
|
{
|
|
T instance = _instances[i];
|
|
if (instance == null) continue;
|
|
if (filter != null && !filter(instance)) continue;
|
|
|
|
float sqrDist = (getPosition(instance) - position).sqrMagnitude;
|
|
if (sqrDist <= sqrRadius)
|
|
_withinRadiusResults.Add(instance);
|
|
}
|
|
return _withinRadiusResults;
|
|
}
|
|
|
|
/// <summary>Call on scene unload to prevent stale references.</summary>
|
|
public static void Clear() => _instances.Clear();
|
|
}
|