-
Notifications
You must be signed in to change notification settings - Fork 43
DisplayKit Examples
Killers0992 edited this page Mar 14, 2026
·
12 revisions
Practical code examples for common DisplayKit use cases.
- Simple Welcome Message
- Player Health Bar (Per-Player)
- Global Server Announcements
- Scoreboard (Toggle Visibility)
- Round Countdown Timer (Global)
- Per-Player Role Display
A simple global welcome message shown to all players.
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;
}
}Individual health bars for each player.
Health bar at 100% (green)
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;
}
}Server-wide announcements that all players see.
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;
}
}Global scoreboard.
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;
}
}- Style Properties - Explore all styling options