-
Notifications
You must be signed in to change notification settings - Fork 6
en Basic UI
UIHandle is the base class for InnoVault's general-purpose UI system. Inheriting from UIHandle allows your code to be automatically managed by the UI system, handling both logical updates and drawing updates.
Compared to tModLoader's UIState/UIElement system, UIHandle is more lightweight and flexible:
- Auto-registration: Just inherit — no need to manually register to interface layers
- Multi-layer rendering: Supports 10 render layers, automatically inserted at different positions in the vanilla UI pipeline
-
Dual-thread updates: Provides draw-thread
Update()and logic-threadLogicUpdate() -
Built-in key state: Automatically tracks mouse left/right button
Pressed/Held/Releasedstates -
Persistent storage: Supports
SaveUIData/LoadUIDatafor persisting UI state -
Render priority: Controls draw order of same-layer UIs via
RenderPriority -
Global hooks: Modify all UI element behavior via
GlobalUIHandle
| Property/Field | Type | Default | Description |
|---|---|---|---|
Active |
bool |
false |
Whether UI is active, must override to implement activation logic |
Texture |
Texture2D |
placeholder | UI main texture, overridable |
LayersMode |
LayersModeEnum |
Vanilla_Mouse_Text |
UI render layer (assigned once at load) |
RenderPriority |
float |
1 |
Same-layer render sort value, higher = on top |
DrawPosition |
Vector2 |
— | Draw position (top-left of rectangle) |
Size |
Vector2 |
— | UI rectangle size |
UIHitBox |
Rectangle |
— | UI collision rectangle |
MousePosition |
Vector2 |
— | Mouse screen position |
MouseHitBox |
Rectangle |
— | Mouse collision rectangle (1x1 pixel) |
hoverInMainPage |
bool |
— | Reserved hover detection field |
keyLeftPressState |
KeyPressState |
— | Left button state (auto-follows current update thread) |
keyRightPressState |
KeyPressState |
— | Right button state (auto-follows current update thread) |
player |
Player |
— | Local player (Main.LocalPlayer shortcut) |
ID |
int |
— | Internal ID (read-only) |
Mod |
Mod |
— | Owning mod |
| Layer | Description |
|---|---|
Vanilla_Mouse_Text |
Mouse text layer (default), topmost |
Vanilla_Interface_Logic_1 |
Interface logic layer |
Vanilla_MP_Player_Names |
Multiplayer player names layer |
Vanilla_Hide_UI_Toggle |
UI hide toggle layer |
Vanilla_Resource_Bars |
Resource bars layer (HP/MP bars) |
Vanilla_Ingame_Options |
In-game options layer |
Vanilla_Diagnose_Net |
Network diagnostics layer |
Mod_MenuLoad |
Mod menu layer (available in main menu) |
Mod_UIModItem |
Mod icon interface layer |
None |
No auto-update, requires manual management |
Note:
LayersModeis only assigned once at load time. Changing it at runtime will have no effect.
| State | Description |
|---|---|
None |
Not pressed |
Pressed |
Just pressed (single frame) |
Held |
Held down continuously |
Released |
Just released (single frame) |
Key state automatically switches based on the current update thread (draw thread reads draw state, logic thread reads logic state).
When LayersMode is None, global key states are not updated. In that case, use instance methods CheckLeftKeyState() / CheckRightKeyState() to get real-time state (can only be called once per update cycle).
Below is a usage example from InnoVaultExample
internal class ExampleUI : UIHandle
{
// Render layer defaults to Vanilla_Mouse_Text, its return value determines
// which update collection the UI will run in. Don't change this at runtime.
public override LayersModeEnum LayersMode => base.LayersMode;
public override Texture2D Texture => Mod.Assets.Request<Texture2D>("Asset/ExampleUIPanel").Value;
internal bool _active;
internal float _sengs;
// Active determines whether the UI will be updated and drawn
public override bool Active => _active || _sengs > 0;
public override void Update() {
// A unified draw origin is important
DrawPosition = new Vector2(Main.screenWidth / 2, Main.screenHeight / 2)
+ new Vector2(0, -600 + 600 * _sengs);
if (_active) {
if (_sengs < 1) _sengs += 0.02f;
}
else {
if (_sengs > 0) _sengs -= 0.02f;
}
}
public override void Draw(SpriteBatch spriteBatch) {
spriteBatch.Draw(Texture, DrawPosition, null, Color.White * _sengs
, 0, Vector2.Zero, 2 * _sengs, SpriteEffects.None, 0);
}
}This example is best viewed alongside ExampleButtonUI — together they demonstrate how different UI elements should interact with each other.
| Method | When Called | Description |
|---|---|---|
Update() |
Every frame (draw thread) | Called before Draw(), handles UI state updates |
LogicUpdate() |
Every frame (logic thread) | Game main loop logic update, not called in main menu |
MenuLogicUpdate() |
Main menu at fixed 60 ticks | Main menu logic update, frame-rate independent |
Draw(SpriteBatch) |
Every frame (draw thread) | Render UI |
OnEnterWorld() |
When entering world | UI initialization |
UnLoad() |
When game unloads | Resource cleanup |
SaveUIData(TagCompound) |
On save | Saved using FullName as key |
LoadUIData(TagCompound) |
On load | Not called if no saved data exists |
UIHandle provides two independent update hooks:
-
Update(): Runs on the draw thread, frame-rate dependent. Suitable for animations, position interpolation, and other visual logic -
LogicUpdate(): Runs on the game logic thread (60 ticks/sec), frame-rate independent. Suitable for data logic
Use the static property UIHandle.IsLogicUpdate to determine which thread you're currently on.
UIHandleLoader provides global key events that can be subscribed to from anywhere:
UIHandleLoader.LeftPressedEvent += () => { /* Left button pressed */ };
UIHandleLoader.LeftHeldEvent += () => { /* Left button held */ };
UIHandleLoader.LeftReleasedEvent += () => { /* Left button released */ };
UIHandleLoader.RightPressedEvent += () => { /* Right button pressed */ };
UIHandleLoader.RightHeldEvent += () => { /* Right button held */ };
UIHandleLoader.RightReleasedEvent += () => { /* Right button released */ };Inherit GlobalUIHandle to modify the behavior of all UI elements:
public class MyGlobalUI : GlobalUIHandle
{
// When key states update
public override void UpdateKeyState() { }
// Before a UI element updates, return false to prevent that element from updating
public override bool PreUIHanderElementUpdate(UIHandle handle) => true;
// After a UI element updates
public override void PostUIHanderElementUpdate(UIHandle handle) { }
// After all UI updates complete
public override void PostUpdataInUIEverything() { }
}| Method | Description |
|---|---|
UIHandleLoader.GetUIHandleInstance<T>() |
Get UI instance of specified type |
UIHandleLoader.GetUIHandleInstance(string name) |
Get UI instance by name |
UIHandleLoader.GetUIHandleOfType<T>() |
Return type-safe UI instance |
UIHandleLoader.GetUIHandleID<T>() |
Get UI's internal ID |
UIHandleLoader.UIHandles |
List of all registered UI instances |
UIHandle ui = Mod.FindUI<ExampleUI>();
UIHandle ui2 = Mod.FindUI("ExampleUI");For interaction between different UI elements, it's recommended to get references via UIHandleLoader.GetUIHandleOfType<T>():
internal class ExampleButtonUI : UIHandle
{
public override void Update() {
if (keyLeftPressState == KeyPressState.Pressed && hoverInMainPage) {
// Get another UI instance by type and modify its state
var mainUI = UIHandleLoader.GetUIHandleOfType<ExampleUI>();
mainUI._active = !mainUI._active;
}
}
}It's recommended to consistently use DrawPosition as your draw origin — this makes it easy to implement overall UI movement, animations, and similar effects.
| Previous | Next |
|---|---|
| Basic TP Entity | SaveContent Save System |