Show live hunter/hider counts in match HUD

Count spawned role variants from a client-side avatar registry in
NetworkPlayerSetup (network objects exist on every machine), so the
counts work without server-only match state. MatchHud binds the two
count texts and rewrites them only on change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-07-03 13:34:41 +02:00
1 parent 67f97df9f0
commit 6434e1c4fc
2 files changed
+38 -2

No files matched your search

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
@@ -116,6 +117,7 @@ public class NetworkPlayerSetup : NetworkBehaviour
public override void OnNetworkSpawn()
{
_active.Add(this); // scoreboard registry (paired with OnNetworkDespawn)
_bodyRenderers = GetComponentsInChildren<SkinnedMeshRenderer>(true);
_painter = GetComponentInChildren<MeshPainter>(true);
_concealment = GetComponentInChildren<ConcealmentMeter>(true);
@@ -177,6 +179,7 @@ public class NetworkPlayerSetup : NetworkBehaviour
public override void OnNetworkDespawn()
{
_active.Remove(this);
_isFound.OnValueChanged -= OnFoundChanged;
}
@@ -377,6 +380,25 @@ public class NetworkPlayerSetup : NetworkBehaviour
return _localViewerIsHunter;
}
// Live scoreboard source. Every match avatar is a network object present on ALL machines, so counting the
// spawned role variants is client-safe - no server-only match state (e.g. _pendingHiders) is needed.
// Hunters = hunter variants; hiders = un-found hider variants (a Classic-found hider leaves the count, a
// Conversion hit despawns the hider object and spawns a hunter one, so both sides update from the registry).
private static readonly List<NetworkPlayerSetup> _active = new List<NetworkPlayerSetup>();
internal static void CountRoles(out int hunters, out int hiders)
{
hunters = 0;
hiders = 0;
for (int i = 0; i < _active.Count; i++)
{
var p = _active[i];
if (!p || !p.scopedModes) continue; // ignore the neutral lobby avatar
if (p.role == PlayerRole.Hunter) hunters++;
else if (!p._isFound.Value) hiders++;
}
}
/// <summary>
/// Places the freshly-spawned owner: role-specific spawn area first, then the shared area (a random
/// point inside the collider), falling back to the deterministic client-id ring. Owner-authoritative -