Skip to content

DisplayKit Examples

Killers0992 edited this page Mar 14, 2026 · 12 revisions

Examples

Practical code examples for common DisplayKit use cases.

Table of Contents


Example 1: Simple Welcome Message

A simple global welcome message shown to all players.

In-Game Preview

Welcome Message Centered welcome message with shadow effect

using System;
using DisplayKit.Elements;
using DisplayKit.Enums;
using LabApi.Events.Arguments.PlayerEvents;
using LabApi.Events.Handlers;
using MEC;
using UnityEngine;
using UnityEngine.UIElements;

public class WelcomeUI : IDisposable
{
	private DisplayCanvas canvas;
	private DisplayText welcomeText;

	public WelcomeUI()
	{
		Debug.Log("WelcomeUI initialized, subscribing to events.");
		ServerEvents.WaitingForPlayers += OnWaitingForPlayers;
		ServerEvents.RoundRestarted += OnRestart;
		PlayerEvents.Joined += OnJoined;
	}

	private void OnWaitingForPlayers()
	{
		canvas = DisplayCanvas.Create();
		canvas.DefaultVisibility = CanvasVisibility.Visible;

		canvas.Flex.Grow = 1f;
		canvas.Background.Color = new Color(0, 0, 0, 0.7f);

		welcomeText = canvas.AddText("Welcome to the Server!");
		welcomeText.Text.Font = FontType.RobotoBold;
		welcomeText.Text.FontSize = 48f;
		welcomeText.Text.Color = Color.white;
		welcomeText.Text.Align = TextAnchor.MiddleCenter;
		welcomeText.Text.TextShadow = new TextShadow
		{
			color = Color.black,
			offset = new Vector2(3, 3),
			blurRadius = 5f
		};

		// Center using flex on canvas
		canvas.Align.AlignItems = Align.Center;
		canvas.Align.JustifyContent = Justify.Center;
	}

	private void OnJoined(PlayerJoinedEventArgs ev)
	{
		canvas?.Spawn(ev.Player.Connection.connectionId);

		Timing.CallDelayed(7f, () =>
		{
			canvas?.Hide(ev.Player.Connection.connectionId);
		}, ev.Player.GameObject);
	}

	private void OnRestart()
	{
		canvas?.Despawn();
		canvas = null;
	}

	public void Dispose()
	{
		ServerEvents.WaitingForPlayers -= OnWaitingForPlayers;
		ServerEvents.RoundRestarted -= OnRestart;
		PlayerEvents.Joined -= OnJoined;
	}
}

Example 2: Player Health Bar (Per-Player)

Individual health bars for each player.

In-Game Preview

Health Bar - Green Health bar at 100% (green)

Health Bar - Low Health Health bar at critical level (red)

using System;
using System.Collections.Generic;
using DisplayKit.Elements;
using DisplayKit.Enums;
using LabApi.Events.Arguments.PlayerEvents;
using LabApi.Events.Handlers;
using LabApi.Features.Wrappers;
using MEC;
using UnityEngine;
using UnityEngine.UIElements;

public class HealthUI : IDisposable
{
	public class PlayerHealthData : IDisposable
	{
		public static PlayerHealthData Create(Player player)
		{
			DisplayCanvas canvas = DisplayCanvas.Create();

			canvas.DefaultVisibility = CanvasVisibility.Visible;
			canvas.SortOrder = 100;
			canvas.Flex.Grow = 1f;
			canvas.Background.Color = new Color(0, 0, 0, 0.7f);

			DisplayElement background = canvas.AddElement();
			background.Position.Position = Position.Absolute;
			background.Position.Top = 50f;
			background.Position.Left = Length.Percent(50f);
			background.Size.Width = 400f;
			background.Size.Height = 30f;
			background.Transform.Translate = new Translate(new Length(-200), new Length(-0));
			background.Background.Color = new Color(0.2f, 0.2f, 0.2f);
			background.Border.Color = Color.white;
			background.Border.Width = 2f;
			background.Border.Radius = 5f;

			DisplayElement fill = background.AddElement();
			fill.Flex.Grow = 1f;
			fill.Background.Color = Color.green;

			DisplayText text = background.AddText("100 / 100");
			text.Text.Font = FontType.RobotoBold;
			text.Text.FontSize = 18f;
			text.Text.Color = Color.white;
			text.Position.Position = Position.Absolute;
			text.Position.Top = Length.Percent(50f);
			text.Position.Left = Length.Percent(50f);
			text.Transform.Translate = new Translate(new Length(-50), new Length(-50));

			canvas.Spawn(player.Connection.connectionId);

			return new PlayerHealthData(player, canvas, background, fill, text);
		}

		public Player Player { get; }
		public DisplayCanvas Canvas { get; }

		public DisplayElement Background { get; }
		public DisplayElement Fill { get; }
		public DisplayText HealthText { get; }

		public float CurrentHealth { get; set; }
		public float CurrentMaxHealth { get; set; }

		public PlayerHealthData(Player player, DisplayCanvas canvas, DisplayElement background, DisplayElement fill, DisplayText healthText)
		{
			Player = player;
			Canvas = canvas;
			Background = background;
			Fill = fill;
			HealthText = healthText;
		}

		public bool NeedsUpdate() => CurrentHealth != Player.Health || CurrentMaxHealth != Player.MaxHealth;

		public void Update()
		{
			Debug.Log("Update values for " + Player.Nickname);

			CurrentHealth = Player.Health;
			CurrentMaxHealth = Player.MaxHealth;

			HealthText.Value = $"{CurrentHealth:F0} / {CurrentMaxHealth:F0}";

			// Update bar width
			float healthPercent = CurrentHealth / CurrentMaxHealth;
			Fill.Size.Width = Length.Percent(healthPercent * 100f);

			// Change color based on health
			if (healthPercent > 0.6f)
				Fill.Background.Color = Color.green;
			else if (healthPercent > 0.3f)
				Fill.Background.Color = Color.yellow;
			else
				Fill.Background.Color = Color.red;
		}

		public void Dispose()
		{
			Canvas?.Despawn();
		}
	}

	public Dictionary<Player, PlayerHealthData> PlayerHealths { get; } = new Dictionary<Player, PlayerHealthData>();

	private CoroutineHandle _handle;

	public HealthUI()
	{
		Debug.Log("HealthUI initialized, subscribing to events.");

		ServerEvents.WaitingForPlayers += OnWaiting;
		PlayerEvents.Joined += OnJoined;
		PlayerEvents.Left += OnLeft;
	}

	private void OnWaiting()
	{
		if (_handle.IsValid)
			Timing.KillCoroutines(_handle);

		_handle = Timing.RunCoroutine(UpdateLoop());
	}

	private IEnumerator<float> UpdateLoop()
	{
		while (true)
		{
			try
			{
				foreach (PlayerHealthData hData in PlayerHealths.Values)
				{
					if (!hData.NeedsUpdate())
						continue;

					hData.Update();
				}
			}
			catch(Exception ex)
			{
				Debug.LogError("Error in HealthUI UpdateLoop: " + ex);
			}

			yield return Timing.WaitForSeconds(0.5f);
		}
	}

	private void OnJoined(PlayerJoinedEventArgs ev)
	{
		PlayerHealthData data = PlayerHealthData.Create(ev.Player);

		PlayerHealths.Add(ev.Player, data);
	}

	private void OnLeft(PlayerLeftEventArgs ev)
	{
		if (!PlayerHealths.TryGetValue(ev.Player, out PlayerHealthData hData))
			return;

		hData.Dispose();

		PlayerHealths.Remove(ev.Player);
	}

	public void Dispose()
	{
		ServerEvents.WaitingForPlayers -= OnWaiting;
		PlayerEvents.Joined -= OnJoined;
		PlayerEvents.Left -= OnLeft;
	}
}

Example 3: Global Server Announcements

Server-wide announcements that all players see.

In-Game Preview

Server Announcement Server announcement banner shown to all players

using System;
using System.Collections.Generic;
using DisplayKit.Elements;
using DisplayKit.Enums;
using LabApi.Events.Arguments.PlayerEvents;
using LabApi.Events.Handlers;
using MEC;
using UnityEngine;
using UnityEngine.UIElements;

public class AnnouncementUI : IDisposable
{
	private DisplayCanvas canvas;
	private DisplayText announcementText;

	private CoroutineHandle _handle;

	public AnnouncementUI()
	{
		Debug.Log("AnnouncementUI initialized, subscribing to events.");
		ServerEvents.WaitingForPlayers += OnWaitingForPlayers;
		ServerEvents.RoundRestarted += OnRestart;
		PlayerEvents.Joined += OnJoined;

		ServerEvents.RoundStarted += OnStarted;
	}

	private void OnWaitingForPlayers()
	{
		canvas = DisplayCanvas.Create();
		canvas.DefaultVisibility = CanvasVisibility.Hidden;
		canvas.SortOrder = 200;

		DisplayElement backgroundPanel = canvas.AddElement();
		backgroundPanel.Position.Position = Position.Absolute;
		backgroundPanel.Position.Top = 100f;
		backgroundPanel.Position.Left = Length.Percent(50f);
		backgroundPanel.Transform.Translate = new Translate(new Length(-250), new Length(0));
		backgroundPanel.Size.Width = 500f;
		backgroundPanel.Size.Height = 80f;
		backgroundPanel.Background.Color = new Color(0, 0, 0, 0.85f);
		backgroundPanel.Border.Color = Color.cyan;
		backgroundPanel.Border.Width = 3f;
		backgroundPanel.Border.Radius = 10f;
		backgroundPanel.Align.AlignItems = Align.Center;
		backgroundPanel.Align.JustifyContent = Justify.Center;

		announcementText = backgroundPanel.AddText("");
		announcementText.Text.Font = FontType.RobotoBold;
		announcementText.Text.FontSize = 24f;
		announcementText.Text.Color = Color.white;
		announcementText.Text.Align = TextAnchor.MiddleCenter;

		canvas.Spawn();
	}

	private void ShowAnnouncement(string message, float duration = 5f)
	{
		if (announcementText == null || canvas == null)
			return;

		if (_handle.IsValid)
			Timing.KillCoroutines(_handle);

		_handle = Timing.RunCoroutine(AnnouncementHandle(message, duration));

	}

	private IEnumerator<float> AnnouncementHandle(string message, float duration)
	{
		announcementText.Value = message;
		canvas.Show();

		yield return Timing.WaitForSeconds(duration);

		canvas.Hide();
	}

	private void OnJoined(PlayerJoinedEventArgs ev)
	{
		ShowAnnouncement($"Player {ev.Player.Nickname} has joined the game!");
	}

	private void OnStarted()
	{
		ShowAnnouncement($"Round started!");
	}

	private void OnRestart()
	{
		canvas?.Despawn();
		canvas = null;
	}

	public void Dispose()
	{
		ServerEvents.WaitingForPlayers -= OnWaitingForPlayers;
		ServerEvents.RoundRestarted -= OnRestart;
		PlayerEvents.Joined -= OnJoined;
		ServerEvents.RoundStarted -= OnStarted;
	}
}

Example 4: Scoreboard

Global scoreboard.

In-Game Preview

Scoreboard Visible Scoreboard shown

using System;
using System.Collections.Generic;
using System.Linq;
using DisplayKit.Elements;
using DisplayKit.Enums;
using LabApi.Events.Arguments.PlayerEvents;
using LabApi.Events.Handlers;
using LabApi.Features.Wrappers;
using MEC;
using UnityEngine;
using UnityEngine.UIElements;

public class ScoreboardUI : IDisposable
{
	public class ScoreboardEntry : IDisposable
	{
		public DisplayElement Element { get; }

		public DisplayText Name { get; }
		public DisplayText Score { get; }

		public ScoreboardEntry(DisplayElement playersList, string name, int score)
		{
			Element = playersList.AddElement();
			Element.Flex.Direction = FlexDirection.Row;
			Element.Align.JustifyContent = Justify.SpaceBetween;
			Element.Spacing.PaddingTop = 5f;
			Element.Spacing.PaddingBottom = 5f;
			Element.Border.BottomWidth = 1f;
			Element.Border.BottomColor = new Color(0.3f, 0.3f, 0.3f);

			Name = Element.AddText(name);
			Name.Text.Color = Color.white;

			Score = Element.AddText($"{score} xp");
			Score.Text.Color = Color.cyan;
			Score.Text.Font = FontType.RobotoMonoBold;
		}

		public void Dispose()
		{
			Element?.Remove();
		}
	}

	public class ScoreData
	{
		public Player Player { get; }

		public int Score { get; set; }

		public ScoreData(Player player, DisplayElement playersList)
		{
			Player = player;
		}
	}

	public Dictionary<Player, ScoreData> Scores { get; } = new Dictionary<Player, ScoreData>();
	public List<ScoreboardEntry> Entries { get; } = new List<ScoreboardEntry>();

	private DisplayCanvas _scoreCanvas;
	private DisplayElement _playersList;

	private CoroutineHandle _handle;

	public ScoreboardUI()
	{
		Debug.Log("ScoreboardUI initialized, subscribing to events.");

		ServerEvents.WaitingForPlayers += OnWaiting;
		ServerEvents.RoundRestarted += OnRestart;
		PlayerEvents.Joined += OnJoined;
		PlayerEvents.Left += OnLeft;
	}

	private void OnWaiting()
	{
		if (_handle.IsValid)
			Timing.KillCoroutines(_handle);

		_handle = Timing.RunCoroutine(UpdateLoop());

		_scoreCanvas = DisplayCanvas.Create();
		_scoreCanvas.DefaultVisibility = CanvasVisibility.Visible;
		_scoreCanvas.SortOrder = 50;

		DisplayElement mainContainer = _scoreCanvas.AddElement();
		mainContainer.Position.Position = Position.Absolute;
		mainContainer.Position.Top = 100f;
		mainContainer.Position.Right = 50f;
		mainContainer.Size.Width = 300f;
		mainContainer.Background.Color = new Color(0, 0, 0, 0.8f);
		mainContainer.Border.Color = Color.white;
		mainContainer.Border.Width = 2f;
		mainContainer.Border.Radius = 10f;

		mainContainer.Spacing.PaddingTop = 15f;
		mainContainer.Spacing.PaddingBottom = 15f;
		mainContainer.Spacing.PaddingLeft = 15f;
		mainContainer.Spacing.PaddingRight = 15f;

		mainContainer.Flex.Direction = FlexDirection.Column;

		// Header
		DisplayElement headerPanel = mainContainer.AddElement();
		headerPanel.Flex.Direction = FlexDirection.Row;
		headerPanel.Align.JustifyContent = Justify.Center;
		headerPanel.Spacing.PaddingBottom = 10f;
		headerPanel.Border.BottomWidth = 2f;
		headerPanel.Border.BottomColor = Color.gray;

		DisplayText titleText = headerPanel.AddText("SCOREBOARD");
		titleText.Text.Font = FontType.RobotoBold;
		titleText.Text.FontSize = 20f;
		titleText.Text.Color = Color.yellow;

		// List headers
		DisplayElement listHeader = mainContainer.AddElement();
		listHeader.Flex.Direction = FlexDirection.Row;
		listHeader.Align.JustifyContent = Justify.SpaceBetween;
		listHeader.Spacing.PaddingBottom = 5f;

		DisplayText nameHeader = listHeader.AddText("Player");
		nameHeader.Text.Font = FontType.RobotoBold;
		nameHeader.Text.Color = Color.gray;

		DisplayText scoreHeader = listHeader.AddText("Score");
		scoreHeader.Text.Font = FontType.RobotoBold;
		scoreHeader.Text.Color = Color.gray;

		// Player list container
		_playersList = mainContainer.AddElement();
		_playersList.Flex.Direction = FlexDirection.Column;
		_playersList.Flex.Grow = 1f;

		_scoreCanvas.Spawn();
	}

	private void OnRestart()
	{
		Entries.Clear();
		Scores.Clear();
		_scoreCanvas?.Despawn();
	}

	private IEnumerator<float> UpdateLoop()
	{
		while (true)
		{
			try
			{
				foreach (ScoreData sData in Scores.Values)
				{
					sData.Score += 1;
				}

				UpdateEntries();
			}
			catch (Exception ex)
			{
				Debug.LogError("Error in ScoreboardUI UpdateLoop: " + ex);
			}

			yield return Timing.WaitForSeconds(0.5f);
		}
	}

	private void OnJoined(PlayerJoinedEventArgs ev)
	{
		ScoreData data = new ScoreData(ev.Player, _playersList);

		Scores.Add(ev.Player, data);

		Entries.Add(new ScoreboardEntry(_playersList, ev.Player.Nickname, data.Score));
		UpdateEntries();
	}

	private void OnLeft(PlayerLeftEventArgs ev)
	{
		if (!Scores.TryGetValue(ev.Player, out ScoreData sData))
			return;

		Scores.Remove(ev.Player);

		Entries.RemoveAll(x => x.Name.Value == ev.Player.Nickname);
		UpdateEntries();
	}

	private void UpdateEntries()
	{
		List<ScoreData> sorted = Scores.Values
			.OrderByDescending(s => s.Score)
			.ToList();

		for (int i = 0; i < Entries.Count; i++)
		{
			ScoreboardEntry entry = Entries[i];
			entry.Name.Value = $"{sorted[i].Player.Nickname}";
			entry.Score.Value = $"{sorted[i].Score} xp";
		}
	}

	public void Dispose()
	{
		ServerEvents.WaitingForPlayers -= OnWaiting;
		ServerEvents.RoundRestarted -= OnRestart;
		PlayerEvents.Joined -= OnJoined;
		PlayerEvents.Left -= OnLeft;
	}
}

Next Steps

Clone this wiki locally