How to Create a Custom UI Panel with TEngine's UIModule: Complete Integration Guide

To create a custom UI panel in TEngine, inherit from the UIWindow base class, apply the WindowAttribute to define the prefab path and layer, override lifecycle methods like OnCreate and OnRefresh, and use UIModule.Instance.ShowUI<T>() to display it.

TEngine's UI framework centers around the UIModule singleton, which manages a hierarchical stack of windows through the UIWindow base class. Creating a custom UI panel requires understanding this relationship and implementing the specific lifecycle hooks provided by the engine's architecture.

Understanding TEngine's UI Architecture

Before writing code, you need to understand three core components that handle window management, rendering order, and lifecycle events.

The UIModule Singleton

The UIModule (located in UnityProject/Assets/GameScripts/HotFix/GameLogic/Module/UIModule/UIModule.cs) serves as the central manager for all UI operations. It maintains a window stack, handles asynchronous loading, manages depth sorting, and controls visibility states. You access it via UIModule.Instance to open, close, or query panels.

The UIWindow Base Class

Every custom panel must inherit from UIWindow (defined in UnityProject/Assets/GameScripts/HotFix/GameLogic/Module/UIModule/UIWindow.cs). This abstract base class provides virtual methods for the window lifecycle—OnCreate, OnRefresh, OnUpdate, and OnDestroy—along with utilities like SetUIFit() for notch-safe layouts.

The WindowAttribute Metadata

The WindowAttribute (found alongside UIWindow.cs) maps your class to a specific prefab asset and configures its behavior. Key properties include:

  • Location: The prefab path under Resources or AssetBundles (e.g., "UI/Prefabs/ExamplePanel")
  • WindowLayer: The UI layer enum (typically UILayer.UI)
  • FullScreen: Boolean indicating if the panel occupies the full screen
  • HideTimeToClose: Delay in seconds before auto-closing (0 for no auto-close)

Step-by-Step Implementation

Follow these steps to create a fully integrated custom UI panel.

1. Create the Script File

Create a new C# script in UnityProject/Assets/GameScripts/HotFix/GameLogic/Module/UIModule/ or your preferred subfolder.

2. Inherit from UIWindow

Declare your class with the UIWindow base type.

3. Apply WindowAttribute

Decorate the class with [WindowAttribute] to specify the prefab location and layer properties.

4. Implement Lifecycle Methods

Override the virtual methods you need:

  • OnCreate(): Called once when the prefab instantiates—cache UI references here
  • OnRefresh(): Called each time the window becomes visible
  • OnUpdate(): Called every frame while the panel is active
  • OnDestroy(): Cleanup listeners and references

5. Reference UI Elements

Use transform.Find() or GetComponent() to cache references to buttons, text fields, and other UI elements during OnCreate.

6. Handle Notch Safety

Call SetUIFit() with liuHaiFit: true on your RectTransform to ensure compatibility with device notches.

Complete Code Example

Here is a production-ready implementation of a custom message panel:

using UnityEngine;
using GameLogic;

[WindowAttribute(
    Location = "UI/Prefabs/ExamplePanel",
    WindowLayer = UILayer.UI,
    FullScreen = false,
    HideTimeToClose = 0)]
public class ExamplePanel : UIWindow
{
    private Text _messageText;
    private Button _closeBtn;

    protected override void OnCreate()
    {
        // Cache components
        _messageText = transform.Find("MessageText").GetComponent<Text>();
        _closeBtn = transform.Find("CloseButton").GetComponent<Button>();
        
        // Bind events
        _closeBtn.onClick.AddListener(() => Close());
        
        // Apply notch-safe fitting
        SetUIFit(GetComponent<RectTransform>(), liuHaiFit: true);
    }

    public void SetMessage(string text)
    {
        if (_messageText != null)
            _messageText.text = text;
    }

    protected override void OnRefresh()
    {
        // Refresh logic called when panel becomes visible
    }

    protected override void OnUpdate()
    {
        // Frame updates while panel is shown
    }

    protected override void OnDestroy()
    {
        // Prevent memory leaks
        if (_closeBtn != null)
            _closeBtn.onClick.RemoveAllListeners();
    }
}

Opening and Closing Your Custom Panel

Use the UIModule API to manage panel visibility from other game logic.

Opening Panels

Call ShowUI<T>() for synchronous opening or ShowUIAsync<T>() for asynchronous loading:

public class Demo : MonoBehaviour
{
    void Start()
    {
        // Open immediately
        UIModule.Instance.ShowUI<ExamplePanel>();
        
        // Or open with data
        ShowMessagePanel("Welcome to TEngine");
    }

    void ShowMessagePanel(string message)
    {
        UIModule.Instance.ShowUI<ExamplePanel>();
        var panel = UIModule.Instance.GetUIAsyncAwait<ExamplePanel>().Result;
        panel?.SetMessage(message);
    }
}

Hiding and Closing Panels

From inside the panel, call instance methods:

public void OnCloseButtonClicked()
{
    Hide();   // Makes invisible but keeps in stack
    // or
    Close();  // Destroys and removes from stack
}

From external scripts, use the generic module methods:

// Hide (preserve instance)
UIModule.Instance.HideUI<ExamplePanel>();

// Close (destroy instance)
UIModule.Instance.CloseUI<ExamplePanel>();

Key Source Files

Understanding these implementation files helps when debugging or extending functionality:

Summary

Creating a custom UI panel in TEngine requires understanding the relationship between UIModule and UIWindow.

  • Inherit from UIWindow and apply [WindowAttribute] to declare prefab paths and layers
  • Override lifecycle methods (OnCreate, OnRefresh, OnUpdate, OnDestroy) to implement panel logic
  • Use UIModule.Instance.ShowUI<T>() and HideUI<T>() to manage visibility through the engine's stack system
  • Reference transform.Find() and SetUIFit() for safe element binding and device compatibility

Frequently Asked Questions

How do I specify which prefab to use for my custom UI panel?

Apply the [WindowAttribute] to your class and set the Location property to the Resource or AssetBundle path, such as [WindowAttribute(Location = "UI/Prefabs/MyPanel")]. The UIModule uses this path to load the GameObject when ShowUI<T>() is called.

What is the difference between Hide() and Close() in TEngine's UIWindow?

Hide() makes the panel invisible but keeps it in the UIModule window stack, preserving its state for quick redisplay. Close() destroys the GameObject and removes it from the stack entirely, requiring a full reload via ShowUI<T>() the next time you need it.

Can I pass data to a UI panel when opening it?

Yes, but not directly through ShowUI<T>(). First call UIModule.Instance.ShowUI<T>() to ensure the panel exists, then use UIModule.Instance.GetUIAsyncAwait<T>() to retrieve the instance and call public methods like SetMessage() to pass initialization data.

Where should I cache UI element references like Buttons and Text components?

Override OnCreate() in your UIWindow subclass and use transform.Find("ButtonName").GetComponent<Button>() to cache references. This method runs once when the prefab instantiates, ensuring components exist before you interact with them.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →