This commit is contained in:
Matt F 2026-05-10 22:26:55 -07:00
parent f7720a9915
commit 6c37e569ab
18 changed files with 1169 additions and 323 deletions

View file

@ -0,0 +1,94 @@
// Assets/_Project/Scripts/UI/Minimap/MinimapEntityRegistry.cs
using System;
using System.Collections.Generic;
using UnityEngine;
namespace TD.UI.Minimap
{
/// <summary>
/// Visual category for a minimap entity. Determines the shape and size of the icon drawn
/// by <see cref="MinimapView"/>. Color is supplied separately by the entity itself so the
/// view does not need to know about ownership / faction mapping.
/// </summary>
public enum MinimapIconKind : byte
{
Enemy,
Tower,
Builder,
}
/// <summary>
/// Anything that wants to appear on the minimap as a dynamic icon implements this. The view
/// reads every getter on each refresh tick, so values may change between reads (movement is
/// fine — no caching needed).
/// </summary>
public interface IMinimapEntity
{
Vector3 WorldPosition { get; }
Color MinimapColor { get; }
MinimapIconKind IconKind { get; }
/// <summary>
/// The entity's diameter (or footprint extent) in world units. The view converts this
/// to pixels using the current minimap zoom, so a 2×2 tower will appear as a 2-tile
/// square on the minimap and adjacent towers will visually touch — matching their
/// world-space relationship. For point-like entities (builders, enemies) the view
/// also enforces a minimum pixel size so they stay visible when fully zoomed out.
/// </summary>
float MinimapWorldSize { get; }
}
/// <summary>
/// Static registry of every entity currently visible on the minimap. Entities register
/// themselves on spawn and deregister on despawn; the <see cref="MinimapView"/> iterates the
/// set on each refresh.
/// </summary>
/// <remarks>
/// Backing store is a <see cref="HashSet{T}"/> so duplicate Register calls are no-ops and
/// Deregister is O(1). Iteration uses the struct enumerator via <see cref="ForEach"/> to
/// avoid the boxing that would happen if we exposed an <see cref="IEnumerable{T}"/>.
///
/// Static lifetime is intentional: the registry survives scene transitions only if entities
/// remember to deregister. NGO's <c>OnNetworkDespawn</c> covers that for towers / builders /
/// enemies. Manual <see cref="Clear"/> is provided for tests and for explicit reset on match
/// teardown if needed.
/// </remarks>
public static class MinimapEntityRegistry
{
private static readonly HashSet<IMinimapEntity> s_entities = new HashSet<IMinimapEntity>();
/// <summary>Number of currently-registered entities. Cheap, no allocation.</summary>
public static int Count => s_entities.Count;
/// <summary>
/// Registers <paramref name="entity"/> for minimap rendering. No-op if already registered.
/// Safe to call from any thread that Unity allows (i.e., the main thread).
/// </summary>
public static void Register(IMinimapEntity entity)
{
if (entity == null) return;
s_entities.Add(entity);
}
/// <summary>Removes <paramref name="entity"/> from the registry. No-op if not present.</summary>
public static void Deregister(IMinimapEntity entity)
{
if (entity == null) return;
s_entities.Remove(entity);
}
/// <summary>
/// Invokes <paramref name="action"/> once per registered entity. Uses HashSet's struct
/// enumerator so iteration is allocation-free (the only allocation is the closure that
/// <paramref name="action"/> may carry, which is the caller's choice).
/// </summary>
public static void ForEach(Action<IMinimapEntity> action)
{
if (action == null) return;
foreach (var e in s_entities) action(e);
}
/// <summary>Drops every registered entity. Tests and explicit match-teardown use only.</summary>
public static void Clear() => s_entities.Clear();
}
}