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 (typicallyUILayer.UI)FullScreen: Boolean indicating if the panel occupies the full screenHideTimeToClose: 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 hereOnRefresh(): Called each time the window becomes visibleOnUpdate(): Called every frame while the panel is activeOnDestroy(): 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:
UIModule.cs: Core manager atUnityProject/Assets/GameScripts/HotFix/GameLogic/Module/UIModule/UIModule.cshandling window stacks and resource loadingUIWindow.cs: Base class atUnityProject/Assets/GameScripts/HotFix/GameLogic/Module/UIModule/UIWindow.csproviding lifecycle hooks and safe-fit helpersWindowAttribute.cs: Metadata decorator defining prefab paths and layer properties
Summary
Creating a custom UI panel in TEngine requires understanding the relationship between UIModule and UIWindow.
- Inherit from
UIWindowand apply[WindowAttribute]to declare prefab paths and layers - Override lifecycle methods (
OnCreate,OnRefresh,OnUpdate,OnDestroy) to implement panel logic - Use
UIModule.Instance.ShowUI<T>()andHideUI<T>()to manage visibility through the engine's stack system - Reference
transform.Find()andSetUIFit()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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →