Skip to content

DisplayKit SendingToPlayers

davidsebesta edited this page Jun 14, 2026 · 9 revisions

Sending to Players

This guide covers how to spawn canvases on all (and specific!) players, as well as controlling their visibility.

Table of Contents


Understanding Canvas Scope

Important

DisplayKit canvases can be either per-player or global. Understanding the difference is crucial.

Per-Player Canvases

  • Each player gets their own unique canvas
  • Changes affect only that specific player's view
  • Use when each player needs unique content
  • Examples: Health bars, inventory, role displays, personal stats
// Per-player canvas - each player gets their own
DisplayCanvas playerCanvas = DisplayCanvas.Create();
playerCanvas.Spawn(playerConnectionId);

Global Canvases

  • Single canvas shared across all spawned players
  • Changes affect ALL observers simultaneously
  • Use for shared UI that everyone sees
  • Examples: Server announcements, countdown timers, scoreboards
// Global canvas - all players see the same thing
DisplayCanvas globalCanvas = DisplayCanvas.Create();
globalCanvas.Spawn(); // Spawns to all current and future players

Comparison

Feature Per-Player Global
Created per player Yes No
Changes affect One player All players
Memory usage Higher Lower
Network traffic Per player Broadcast once
Best for Personal UI Shared UI
Spawn method Spawn(connectionId) Spawn()

Spawning to All Players

Global Canvas (All Players)

// Spawn to all current AND future players (global canvas)
canvas.Spawn();

// Canvas will be visible by default based on DefaultVisibility
canvas.DefaultVisibility = CanvasVisibility.Visible;

Example: Server Announcement

public partial class AnnouncementSystem : CustomEventsHandler
{
    private DisplayCanvas _announcementCanvas;
    private DisplayText _announcementText;

    public override void OnServerWaitingForPlayers()
    {
        _announcementCanvas = DisplayCanvas.Create();
        _announcementCanvas.DefaultVisibility = CanvasVisibility.Hidden;

        // ... setup UI elements ...

        _announcementText = _announcementCanvas.AddText();

        _announcementText.Text.Font = FontType.RobotoBold;
        _announcementText.Text.FontSize = 32f;
        _announcementText.Text.Color = Color.white;
        _announcementText.Text.Align = TextAnchor.MiddleCenter;

        _announcementText.Spacing.MarginBottom = 10f;

        // Global canvas - spawns to all players (current and future)
        _announcementCanvas.Spawn();
    }

    public void ShowAnnouncement(string message)
    {
        // Update and show
        _announcementText.Content = message;
        _announcementCanvas.Show();
    }

    public void HideAnnouncement()
    {
        _announcementCanvas.Hide();
    }
}

Late Joiners

When using canvas.Spawn() without parameters:

  • Current players receive the canvas immediately
  • New players (late joiners) automatically receive the canvas when they connect
  • This makes it ideal for persistent UI like round timers or server info

Spawning to Specific Players

Per-Player Canvas

// Spawn to specific player by connection ID (per-player canvas)
canvas.Spawn(connectionId);

// Spawn to specific player by ReferenceHub
ReferenceHub player = ...;
canvas.Spawn(player);

Example: Health Bar Per Player

public class HealthBarUI : CustomEventsHandler
{
    private Dictionary<int, PlayerHealthData> playerHealthBars = new Dictionary<int, PlayerHealthData>();

    public override void OnPlayerJoined(PlayerJoinedEventArgs ev)
    {
        // Create unique canvas for this player
        DisplayCanvas canvas = DisplayCanvas.Create();
        canvas.DefaultVisibility = CanvasVisibility.Visible;

        // ... setup health bar UI ...

        // Spawn only to this player
        canvas.Spawn(ev.Player);

        playerHealthBars[ev.Player] = new PlayerHealthData(...);
    }

    public void UpdatePlayerHealth(Player player)
    {
        // Update only this player's health bar
        if (playerHealthBars.TryGetValue(player, out PlayerHealthData data))
        {
            data.HealthText.Content = $"{player.Health:F0} / {data.MaxHealth:F0}";
        }
    }
}

Note: For a full example, see Examples page.


Visibility Control

Show/Hide for All Observers

// Show canvas to all observers
canvas.Show();

// Hide canvas from all observers
canvas.Hide();

// Set visibility explicitly
canvas.SetVisibility(true);  // Show
canvas.SetVisibility(false); // Hide

Show/Hide for Specific Player

// Show to specific player
canvas.Show(connectionId);
canvas.Show(player);
canvas.Show(referenceHub);

// Hide from specific player
canvas.Hide(connectionId);
canvas.Hide(player);
canvas.Hide(referenceHub);

// Set visibility explicitly
canvas.SetVisibility(player, true);
canvas.SetVisibility(player, false);

Example: Toggle Scoreboard

public class CanvasShowKeybind : CustomEventsHandler
{
    private const int KeybindId = 1;

    private DisplayCanvas _scoreboardCanvas;

    public override void OnServerWaitingForPlayers()
    {
        ServerSpecificSettingsSync.DefinedSettings = new ServerSpecificSettingBase[]
        {
                new SSGroupHeader("Misc"),
                new SSKeybindSetting(KeybindId, "Show canvas", KeyCode.Tab, hint: $"Show super cool canvas"),
        };

        ServerSpecificSettingsSync.SendToAll();

        ServerSpecificSettingsSync.ServerOnSettingValueReceived += ProcessUserInput;


        _scoreboardCanvas = DisplayCanvas.Create();
        _scoreboardCanvas.DefaultVisibility = CanvasVisibility.Hidden;

        // setup the scoreboard for your needs...
    }

    private void ProcessUserInput(ReferenceHub sender, ServerSpecificSettingBase setting)
    {
        switch (setting.SettingId)
        {
            case KeybindId when setting is SSKeybindSetting keybind:
                {
                    if (keybind.SyncIsPressed)
                        _scoreboardCanvas.Show(sender);
                    else
                        _scoreboardCanvas.Hide(sender);
                }
                break;

        }
    }
}

Default Visibility

// Canvas starts visible when constructed
canvas.DefaultVisibility = CanvasVisibility.Visible;

// Canvas starts hidden when constructed
canvas.DefaultVisibility = CanvasVisibility.Hidden;

Common Patterns

Global UI Template

public class GlobalTimerUI : CustomEventsHandler
{
    private DisplayCanvas _timerCanvas;
    private DisplayText _timerText;

    public override void OnServerRoundStarted()
    {
        _timerCanvas = DisplayCanvas.Create();
        _timerCanvas.DefaultVisibility = CanvasVisibility.Visible;

        // ... setup timer UI ...

        _timerText = _timerCanvas.AddText();
        // setup the text...

        // Spawn to ALL players (current and future)
        _timerCanvas.Spawn();
    }

    public void UpdateTime(int secondsRemaining)
    {
        // Update affects ALL players simultaneously
        _timerText.Content = secondsRemaining.ToString();
    }
}

Per-Player UI Template

public class RoleDisplayUI : CustomEventsHandler
{
    private Dictionary<Player, DisplayCanvas> _playerCanvases = new Dictionary<Player, DisplayCanvas>();

    public override void OnPlayerJoined(PlayerJoinedEventArgs ev)
    {
        // Create canvas for this specific player
        DisplayCanvas canvas = DisplayCanvas.Create();
        canvas.DefaultVisibility = CanvasVisibility.Visible;

        // ... setup role display ...

        // Spawn ONLY to this player
        canvas.Spawn(ev.Player);

        _playerCanvases[ev.Player] = canvas;
    }

    public override void OnPlayerLeft(PlayerLeftEventArgs ev)
    {
        // Clean up this player's canvas
        if (_playerCanvases.TryGetValue(ev.Player, out DisplayCanvas canvas))
        {
            canvas.Destroy();
            _playerCanvases.Remove(ev.Player);
        }
    }
}

Next Steps

Clone this wiki locally