reckfellENGINE

UI and signals

A button counter, subscription cleanup, tree reentry and keyboard focus.

On this page

A signal reports an event, such as a button press. In C#, subscribe a method to the corresponding event rather than checking the button every frame.

1. Create the interface

Create a separate document with a Control root, name it Counter and save as Counter.rscene. Control elements are designed for interfaces, with dimensions, anchors and layout rules.

Text
Counter (Control)
├── CountLabel (Label)
└── AddButton (Button)

Select the root and choose Full Rect in the Layout tools. Keep the children anchored at the top left. Set these Layout properties in the inspector:

ObjectPositionSizeText
CountLabel(32, 32)(200, 40)0
AddButton(32, 96)(200, 48)Add +1

If the inspector shows Offsets, the button's equivalent bounds are Left 32, Top 96, Right 232, Bottom 144. This document needs no camera or light.

2. Add Counter.cs

Attach Scripts/Counter.cs to the Counter root, not the button. Use this complete code:

C#
using Reckfell;

public partial class Counter : Control
{
    private int _count;
    private Label _label = null!;
    private Button _button = null!;

    public override void _EnterTree()
    {
        _label = GetEntity<Label>("CountLabel");
        _button = GetEntity<Button>("AddButton");
        _button.Pressed += AddOne;
        _label.Text = _count.ToString();
    }

    public override void _ExitTree()
    {
        if (ReckfellObject.IsInstanceValid(_button))
            _button.Pressed -= AddOne;
    }

    private void AddOne()
    {
        _count += 1;
        _label.Text = _count.ToString();
    }
}

Save, build C# and run the current document using F6. Press the button several times: the label should show 1, 2, 3 and so on.

How the pieces connect

_count stores an integer and starts at zero. _label and _button hold child references. On tree entry, _EnterTree() finds the already constructed children and connects the handler. If you need data calculated by a child's _Ready(), use the parent's _Ready(): children may not be ready during _EnterTree().

Pressed += AddOne means “call AddOne when the button reports a press.” Write AddOne without parentheses here: you are passing an event handler rather than calling it immediately.

_count += 1 increments the counter. ToString() converts the number to text for Label.Text. private marks a field or method as internal to this class. null! allows later initialization; it does not create a Label or fix a misspelled CountLabel name.

Why the subscription has a matching removal

Pressed -= AddOne disconnects the same method connected with +=. _ExitTree() performs this cleanup: an interface removed from the level stops receiving events. IsInstanceValid() also accounts for the button being deleted separately.

Adding the same Counter back with AddChild() makes _EnterTree() reconnect the handler and preserves _count. This differs from deletion with QueueFree(): a new instance starts at zero. The _EnterTree() / _ExitTree() pair also works on reentry, when _Ready() does not repeat automatically.

Do not connect Pressed both through this code and through the editor: one press could add two. Do not subscribe every frame in _Process(). Use a named method when you need to unsubscribe; a new lambda passed to -= is not the previously connected lambda.

Check mouse and keyboard activation

  1. After launch, click the button three times: the label should read 3.
  2. Move focus to the button with Tab and activate ui_accept — Enter or Space by default. The counter should increase by one.
  3. If the project changes built-in input actions, check ui_accept in Input Map. The button also needs an enabled focus mode.

Use Pressed for one action per activation. For a switch, use Toggle Mode and Toggled; do not reconstruct its state by counting clicks.

SymptomCheck
GetEntity fails on startupThe script is attached to Counter; its children are named exactly CountLabel and AddButton, with the correct types
One press adds twoThe editor also connects the signal, or other code repeats the subscription
The button is visible but does not respondCheck Disabled, an overlapping Control, and its Mouse Filter
Tab does not focus the buttonCheck its focus mode, visibility and the ui_focus_next action
Access fails after button deletionDo not retain an unchecked native reference for a later call; match subscriptions to lifetimes

A separate screen or a HUD

This document is a separate screen. In a game with a moving world camera, a HUD usually goes in a CanvasLayer containing a Control tree. This keeps the camera from moving the counter off-screen.

Use containers such as VBoxContainer and HBoxContainer, plus anchors, for resizable layouts instead of scaling text with Scale.

The counter resets on each run: this example has no disk persistence. Reference: Control, Button, Label.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.