Skip to content

HUD Modules

Minh edited this page Aug 16, 2026 · 2 revisions

0. Before you read this

You might want to read the Modules page first, since HUD modules are also modules. Or I guess you can grab an example below and start messing around, your choice.

1. An overview

Here's the class hierarchy of HUD modules:

Module <--is-- HudModule <--is-- FramedHudModule <--is-+-- TextHudModule
                                                       |
                                                       +-- BoundedValueHudModule

HudModule

  • Implements UI component dragging (wraps UI content in a draggable shell)
  • Subclasses must create the UI content by implementing protected abstract GameObject CreateHudObject();
  • You can't override OnEnable and OnDisable as they're sealed, but must instead use OnHudModuleEnable and OnHudModuleDisable

FramedHudModule

  • Implements a frame (background), whose color syncs with that of vanilla HUD elements and can be toggled, as the UI component of a HudModule
  • Subclasses must create the content that sits on the background by implementing protected abstract GameObject CreateContentObject();

TextHudModule

  • Implements text on a FramedHudModule
  • Subclasses only need to update the Text property (and optionally DisplayIcon)

BoundedValueHudModule

  • Implements progress bar-style indicators for stats that are capped
  • Subclasses must specify the Bound (upper limit) and update the Value (and optionally more fields)

2. Example: Making an HP indicator

We go from the higher level ones, as they're easier

Using TextHudModule

using UnityEngine;
using ThornClient.HUD;
using ThornClient.Managers;

namespace ThornExamples;

public class TextHpIndicator : TextHudModule {
    // Search tags and icon like a normal Module...
    public override string[] Tags => ["health", "hit points", "blood", "fuel"];
    public override Sprite Icon => AssetManager.Get<Sprite>(HudManager.BundleKey, "plus_thick");

    // Constructor. You can put setting initializations here...
    public TextHpIndicator() : base("thornExamples.textHp", "Example text HP", "Shows current health") {
    }

    public override void OnUpdate() {
        var nm = NewMovement.Instance;
        if (nm == null) return;
        Text = $"{nm.hp}/100";
    }
}

Using BoundedValueHudModule

using UnityEngine;
using ThornClient.HUD;
using ThornClient.Managers;

namespace ThornExamples;

public class BoundedValueHpIndicator : BoundedValueHudModule {
    public override string[] Tags => ["health", "hit points", "blood", "fuel"];
    public override Sprite Icon => AssetManager.Get<Sprite>(HudManager.BundleKey, "plus_thick");

    // We supply a bound and display name in the constructor
    public BoundedValueHpIndicator() : base("thornExamples.boundedValueHp", "Example Bounded-value HP",
        "Shows current health", bound: 100, decimalPlaces: 0, displayName: "Health") {
    }

    public override void OnUpdate() {
        var nm = NewMovement.Instance;
        if (nm == null) return;

        // Update the main value bar
        Value = nm.hp;

        // Bound reduction indicates a temporary decrease. We use it for showing hard damage
        BoundReduction = nm.antiHp;
    }
}

Using FramedHudModule

With this, you can actually design your UI! You probably want to use the Unity editor to create it, for your sanity.

You want to subclass FramedHudModule and implement this method. It should return the GameObject with the UI element (containing controller components if needed).

protected abstract GameObject CreateContentObject();

Things to remember:

  • The content UI must have a preferred size. You can use a ContentSizeFitter, a Layout element, or a Layout group for this.
  • If your item is a single horizontal row, we recommend the height to be 45 so it aligns well with vanilla's and Thorn's HUD items

Because it's a bit troublesome to create a new UI for an example, we've carefully commented the Fist module to serve as an example.

Using HudModule

Use this only when it's never ever helpful to have a background. For example you could use this for crosshair indicators.

To use this, you subclass and implement this method

protected abstract GameObject CreateHudObject();

3. Some reusable components

We won't go into too much detail here, but these might make your life a bit easier:

  • BatchBoolSettingVisibilitySyncer: For setting visibility of children of a GameObject according to Thorn Setting<bool>s
  • ColorSettingSyncer: For setting the color of an Image according to Thorn Setting<Color>s
  • HudVariantColorSyncer: For setting the color of an Image according to the game's variant color