Skip to content

UI scripting

Drive UI from C#: show and hide menus, click buttons, update a health bar, swap or tint an image, move and resize elements. This page also covers what you can't script yet — worth reading before you design a settings menu around it.

Buttons

A Button's click isn't a C# event you subscribe to from elsewhere — it's a method you override in a script attached to the same entity as the Button:

csharp
using JoystickEngine;

public class ResumeButtonBehavior : GameEntity
{
    protected override void OnClick()
    {
        Canvas.Find("PauseMenu")!.Visible = false;
    }
}

OnClick fires once per completed click on this entity's own UIButtonComponent. Every button entity needs its own small script — there's no single script somewhere else that all your buttons report to.

You can still enable or disable a button from anywhere that has a reference to it:

csharp
UIButtonComponent? quitButton = someEntity.GetComponent<UIButtonComponent>();
if (quitButton != null)
    quitButton.Interactable = false;   // greys it out, ignores clicks

Showing and hiding menus

csharp
Canvas.Find("PauseMenu")!.Visible = true;

Canvas.Find(name) searches the whole scene for a Canvas entity with that name and returns its Canvas component, or null if there's no match — check for null (or use ! only when you're sure it exists, as in the pause-menu example above). Setting Visible is exactly the same as toggling the Visible checkbox in the Inspector: it also hands input focus to (or releases it from) that canvas.

csharp
public class GameManager : GameEntity
{
    protected override void OnUpdate(float ts)
    {
        if (Input.WasKeyPressedThisFrame(KeyCode.Escape))
        {
            Canvas? pause = Canvas.Find("PauseMenu");
            if (pause != null)
                pause.Visible = !pause.Visible;
        }
    }
}

Progress bars and fills

csharp
public class Player : GameEntity
{
    private UIProgressBar? m_HealthBar;
    private float m_MaxHealth = 100.0f;
    private float m_Health = 100.0f;

    protected override void OnCreate()
    {
        m_HealthBar = FindEntityByName("HealthBar")?.GetComponent<UIProgressBar>();
    }

    public void TakeDamage(float amount)
    {
        m_Health = System.Math.Max(0.0f, m_Health - amount);
        if (m_HealthBar != null)
            m_HealthBar.Value = m_Health / m_MaxHealth;
    }
}

Setting Value (0 to 1) is all you need to do — the child entity named Fill (see Components) resizes itself automatically, the same way it does when you drag the Value slider in the Inspector.

Tinting and swapping images

csharp
UIImage? panel = someEntity.GetComponent<UIImage>();
if (panel != null)
{
    panel.Color = new Vector4(1.0f, 0.3f, 0.3f, 1.0f);   // tint red
    panel.Source = "Textures/AlertPanel.png";             // swap the texture
}

Source is an asset-relative path — the same kind of path you'd see in the Inspector's Texture slot. The change takes effect on the next frame.

Moving and resizing elements

csharp
RectTransform? panel = someEntity.GetComponent<RectTransform>();
if (panel != null)
{
    panel.OffsetMin = new Vector2(20.0f, 20.0f);
    panel.OffsetMax = new Vector2(500.0f, 100.0f);
}

Every field in the Rect Transform section — AnchorMin, AnchorMax, OffsetMin, OffsetMax, Pivot, Scale, and RaycastTarget — is readable and writable from script, the same way you'd drag them in the Inspector. Rotation is in degrees, matching what you see in the Inspector, not the radians the component stores internally:

csharp
panel.Rotation = 15.0f;   // tilt 15 degrees

A common use: a notification or tooltip that resizes itself to fit content, or a panel that slides or grows into view by animating OffsetMin/OffsetMax over a few frames. That's the shape of a slide-in notification banner: capture the resting position once, nudge away from it to set a hidden starting point, then interpolate back to it every frame until it arrives — either in OnUpdate with your own elapsed-time bookkeeping, or as a coroutine, which is usually the less fiddly way to write exactly this kind of "wait, then animate toward a target" logic.

What you can't script yet

UIButtonComponent, UIProgressBar, UIImage, Canvas, and RectTransform have C# classes today; Slider, Toggle, Layout Group, Mask, and Scroll Rect don't yet. That means, right now, a script cannot:

  • Read a Slider's or Toggle's current value, or find out when one changes
  • React to a Layout Group, Mask, or Scroll Rect doing anything

For a settings menu, that means: build the sliders/toggles for how they should look, but don't rely on reading their value back from script yet — for anything a script genuinely needs to react to (volume changing, a difficulty toggle), drive it the other way for now: a Button the player confirms with, whose OnClick reads whatever your own data source is, rather than reading the widget itself.

See also

  • Components — every field mentioned above, including which ones are and aren't covered by this page
  • Coroutines — running code across multiple frames: waits, delays, and animating a value smoothly over time
  • Session — carrying a value (a selected settings tab, an equipped item) across a scene change, so UI in a new scene can pick up where the last one left off