5.6 KiB
UI System
Scene structure
RootUI (Control, fullscreen, always visible)
├── DebugMenu
├── PauseMenu
│ ├── PauseSettings
│ └── PauseMain
├── GameMenu
│ ├── GameMenuPartyTab
│ └── GameMenuItemsTab
└── VNTextbox
RootUI is a permanent child of RootScene and registers itself with the UI singleton on _enter_tree. Access its children via UI.TEXTBOX, UI.DEBUG_MENU, and UI.GAME_MENU.
UI singleton
UI (autoload) is the global access point.
| Accessor | Returns | Notes |
|---|---|---|
UI.TEXTBOX |
VNTextbox |
Bottom-screen dialogue box |
UI.DEBUG_MENU |
DebugMenu |
Dev scene-jump overlay |
UI.GAME_MENU |
GameMenu |
JRPG-style in-game menu |
UI.dialogueActive |
bool |
true for the entire duration of a DialogueAction, including line transitions |
dialogueActive is set by DialogueAction — it is broader than just "textbox visible." Movement blocks on both flags: UI.dialogueActive prevents movement even while the textbox is briefly hidden between lines.
VNTextbox
Bottom-anchored PanelContainer that reveals text character-by-character and paginates when content overflows 4 lines.
Showing text from code:
# Fire-and-forget
UI.TEXTBOX.setText("Hello world.")
# Await player dismiss — use this in cutscene callables
await UI.TEXTBOX.setTextAndWait("Hello world.")
Flow:
setTextresets reveal state and sets new text; textbox becomes visible automatically- Player holds
interactto speed up reveal; press again after reveal completes to advance page or close textboxClosingsignal fires when the last page is dismissedsetTextAndWaitawaits that signal before returning
Input guard: EntityMovement._canMove() returns false while !UI.TEXTBOX.isClosed. Don't set text without also expecting movement to be blocked.
Signal: textboxClosing — emitted once per setTextAndWait call when player dismisses.
AdvancedRichText
RichTextLabel subclass (@tool) used inside VNTextbox. Handles:
- Smart word-wrap (
TextServer.AUTOWRAP_WORD_SMART) - Pagination via
maxLines/startLineexports - Inline input icons:
[input action=interact]text[/input]→ replaced with the icon image fromres://ui/input/{action}.tres - Optional auto-translation via Godot's
tr()
Supported icon actions: interact, pause, debug, up, down, left, right.
ClosableMenu
Base class for any togglable panel. Extends Control; isOpen drives visible.
menu.open() # shows, emits opened
menu.close() # hides, emits closed
menu.toggle()
Signals: opened, closed.
All new menus that need standard show/hide behaviour should extend ClosableMenu.
Game menu
JRPG-style in-game menu at res://ui/gamemenu/. Open with the menu input (Tab on keyboard, Y on controller). Blocks player movement while open via EntityMovement._canMove().
Structure:
- Left sidebar:
ItemListtab selector (Party, Items) - Right panel: active tab content
| Tab | Content |
|---|---|
| Party | One card per PartyMember — name, status, HP/MP, ATK/DEF/SPD/MAG/LCK |
| Items | One row per ItemStack in PARTY.BACKPACK — item name + quantity |
Both tabs are populated dynamically on open; call refresh() on the active tab directly if data changes while the menu is already open.
Key methods on GameMenu:
| Method | Effect |
|---|---|
open() |
Shows menu, refreshes active tab, grabs sidebar focus |
close() |
Hides menu |
isOpen() -> bool |
Visibility state |
ui_cancel or menu closes the menu. The menu input opens it only when UI.dialogueActive is false and the textbox is closed.
To add a new tab: add a value to GameMenu.Tab, create a tab scene/script under ui/gamemenu/, instance it in GameMenu.tscn as a sibling of the other tabs, add an @export for it in GameMenu.gd, and add the match branch in _selectTab().
Pause menu
PauseMenu wraps PauseMain (item list) and PauseSettings (settings tabs).
| Method | Effect |
|---|---|
PauseMenu.open() |
Shows container, opens PauseMain |
PauseMenu.close() |
Hides everything |
PauseMenu.isOpen() -> bool |
Visibility state |
ui_cancel inside the pause menu: if PauseSettings is open, closes it and reopens PauseMain; otherwise closes the whole menu.
Stub:
Pause.gd(the singleton) has its logic commented out. Pause menu is not yet wired to actual game-pause state.
Settings menu
SettingsMenu is a shared tabbed component instanced in both MainMenuSettings and PauseSettings. Three tabs: Gameplay, Sound, Graphics — currently contain placeholder labels only.
Debug menu
DebugMenu (toggle via debug input — F1) provides four scene-jump buttons:
| Button | Action |
|---|---|
| Overworld | SCENE.setScene(OVERWORLD) |
| Battle | SCENE.setScene(BATTLE) |
| Cooking | SCENE.setScene(COOKING) |
| Initial | SCENE.setScene(INITIAL) |
Access via UI.DEBUG_MENU. Starts hidden; isClosed getter/setter controls visibility.
Theme & assets
- Global theme:
res://ui/UI Theme.tres— applied to all UI nodes - Input icons:
res://ui/input/{action}.tres— one file per action name - Font: configured globally in project settings
Adding a new menu
- Create a scene whose root extends
ClosableMenu(orControlif open/close isn't needed) - Add it as a child of
RootUI.tscn - Export a typed reference on
RootUI.gdand wire it in the Inspector - Expose via a getter on
UISingleton.gdif other systems need access (follow theTEXTBOX/DEBUG_MENUpattern)