PlayerColorAssigner
Tsvrc.Player.PlayerColorAssigner assigns every player a stable, visually distinct
color from an 82-entry palette, one slot per player up to VRChat's absolute maximum
instance size (80 is the
configurable hard cap; the world author and instance creator can each still join on top
of that, for 82 in the worst case), keyed off their numeric player ID so every client
independently resolves the same color for the same player with no per-lookup network
traffic. See Give each player a unique color for a
task-oriented walkthrough.
Usage
Call Initialize() once per instance start, before any GetColor call. Multiple
consumers that need matching colors should share one PlayerColorAssigner instance
rather than each deriving their own: the whole point of the synced shuffle order is that
everyone agrees.
assigner.Initialize();
Color c = assigner.GetColor(TsPlayer.GetPlayerID(somePlayer));
Why the palette is shuffled, and how
A player's raw color slot is just their numeric player ID modulo the palette size, cheap
and deterministic, but it means the nth player to ever join any instance always gets
the same slot, so two friends who happen to join in the same relative order across
sessions always see the same two colors paired together. Initialize fixes that by
generating a random permutation of palette indices (a synced byte[] _order) once per
instance, so the same raw slot maps to a different actual color each session. Only the
owner actually shuffles and broadcasts it: a non-owner's own Initialize call just
ensures a sane identity-order default (_order[i] = i) is in place locally until the
owner's real order arrives, so GetColor is never called against an uninitialized
array.
Edge cases worth knowing
GetColornever throws: an empty or missing palette returnsColor.white, and a numeric ID that doesn't parse (seeTsPlayer.GetNumericPlayerId) simply resolves to slot0.- Calling
GetColorbeforeInitialize(or before the owner's synced order has arrived) is safe. The palette-index modulo alone still produces a color, just not yet the session-shuffled one. - The palette itself is a fixed, hand-tuned hue sweep (two passes around the color wheel at alternating saturation/value) chosen for maximum distinctness between adjacent slots, not a generated gradient. Changing its length changes the modulo divisor for every ID.