Skip to main content

Show player positions on an overlay

How to render a live minimap-style overlay of tracked players, using PlayerPositionOverlay (itself a PlayerTracker, no separate tracker component needed) and RasterPlayerMarkerRenderer.

Steps​

  1. Add a PlayerPositionOverlay to your scene and register it as a construct, so codegen calls TsConstruct on it at startup. The renderer needs no registration: neither PlayerMarkerRenderer nor RasterPlayerMarkerRenderer uses TsStart or _ts, so drop one into the scene and assign it.

  2. Subclass RasterPlayerMarkerRenderer to decide what a marker looks like:

    public class DotMarkerRenderer : RasterPlayerMarkerRenderer
    {
    public override void DrawMarker(Color32[] pixelBuffer, int textureWidth, int textureHeight,
    int pixelX, int pixelY, bool isLocalPlayer, float headingDegrees, string playerId)
    {
    Color32 color = isLocalPlayer ? Color.green : Color.red;
    TextureGraphics2D.DrawCircleToBuffer(pixelBuffer, textureWidth, textureHeight, pixelX, pixelY, 4, color);
    }
    }
  3. Wire the output texture of the renderer to a RawImage in your UI, then configure and start the overlay:

    public class MinimapController : TsBehaviour
    {
    [SerializeField] private PlayerPositionOverlay _overlay;
    [SerializeField] private DotMarkerRenderer _renderer;
    [SerializeField] private Transform _mapOrigin;

    protected override void TsStart()
    {
    _renderer.Setup(width: 256, height: 256, worldOrigin: _mapOrigin.position, unitsPerGridX: 4f, unitsPerGridZ: 4f);
    _overlay.Renderer = _renderer;
    _overlay.StartOverlay(new string[0]);
    }
    }
  4. Add or remove players while it runs with the tracking methods the overlay inherits, _overlay.AddTrackedPlayers(playerIds) and RemoveTrackedPlayers(playerIds). The renderer picks up the change on its next tick with no extra wiring.

If only a subset of players should ever appear (not everyone in the instance), start the overlay with that explicit ID list instead of an empty one.

Swapping the drawing style​

RasterPlayerMarkerRenderer handles the texture, pixel buffer, and flush timing; only DrawMarker decides the visuals, so a different look (a triangle that rotates with heading, say) is a different DrawMarker body, not a different setup:

public override void DrawMarker(Color32[] pixelBuffer, int textureWidth, int textureHeight,
int pixelX, int pixelY, bool isLocalPlayer, float headingDegrees, string playerId)
{
TextureGraphics2D.DrawTriangleToBuffer(pixelBuffer, textureWidth, textureHeight, pixelX, pixelY, 5, 5, headingDegrees, someColor);
}

Skipping the raster renderer​

If you're not drawing to a texture at all (driving 3D world-space marker objects instead, for example), implement PlayerMarkerRenderer directly instead of its raster subclass. PlayerPositionOverlay only calls the two methods of the base class.

Why this shape​

PlayerPositionOverlay is a pure backend that never draws anything itself. See its reference page for why presentation is split out into a pluggable renderer, and what the two independent tick loops (position resolution vs. blink) cost on their own.