Add a menu screen¶
Designer · 45 min · No code
Result: a new entry in the in-game menu, Bestiary, that opens your own screen. The gamepad and keyboard navigate it, B / Escape returns to the menu, and the character cannot move or jump while it is open.
Before you start¶
- UI shell and HUD: layers, activatable widgets and input modes.
- Restyle the menus lists the shared button and text styles to reuse.
How a menu screen works¶
XenoCore's menus use Epic's CommonUI through Lyra:
- A screen is an activatable widget: a widget that knows when it is on top, where focus goes,
and whether it handles Back. Blueprint screens derive from Lyra's
LyraActivatableWidget. - Screens are pushed onto a layer, a stack of screens. Menus go on the layer tagged
UI.Layer.Menu(a gameplay tag). Only the top screen of a layer is shown, and Back pops it off to reveal the one underneath. - The menu (
UserInterface/W_XenoCore_MenuShell) is itself a screen on that layer.Input/Abilities/GA_XenoCore_ToggleMenupushes it when the player presses the menu button. - The shell has no hand-placed buttons. Each time it opens it builds one button per menu entry, from Project Settings › Game › XenoCore UI › Menu Entries. An entry is data: a label, and either a screen to push or an action to run. So adding a screen to the menu is a settings change, not an edit to the shell.
- A screen's Input Config decides the input mode. Menu mode switches off the gameplay input contexts, so the character stops responding while the screen is up.
The Party, Equipment and Items screens are all built this way, as Blueprints. Use
UserInterface/W_XenoCore_PartyManager as your reference when something is unclear.
Steps¶
1. Create the screen¶
- In
MyGame/UI, right-click and choose User Interface › Widget Blueprint. PickLyraActivatableWidgetas the parent class. Name itW_Bestiary. - Open it. In the Designer, build the layout: a background, a title, and a vertical box of
buttons or a list. Use
W_XenoCore_MenuButtonorW_XenoCore_TabButtonfromUserInterface/Foundationso it matches the rest of the menus. - Any text or image you lay over a button must have Visibility set to Hit Test Invisible (or Self Hit Test Invisible for a panel).
Fails silently: a label drawn over a button steals the pad's A press
With the default input settings, pad A on a focused button is delivered as a click at the button's centre, routed by what is under that point. A visible text block sitting over the button receives the click instead, and the button never fires. The mouse works if you click beside the text, so it looks intermittent. Make every overlay Hit Test Invisible.
2. Set its class defaults¶
Click Class Defaults and set (the categories are Lyra's and CommonUI's):
| Setting | Value | Why |
|---|---|---|
| Input › Input Config | Menu |
Switches gameplay input off while the screen is up |
| Back › Is Back Handler | ticked | Without it, B / Escape does nothing on this screen |
| Back › Is Back Action Displayed In Action Bar | ticked | Shows the Back glyph in the footer |
Then override Get Desired Focus Target (under Functions › Override) and return the widget that should have the cursor when the screen opens, such as the first button.
Fails silently: no focus target
Without Get Desired Focus Target a gamepad player opens the screen with nothing selected, and the stick does nothing. The mouse still works, so you may not notice at your desk.
3. Bind your buttons in Construct¶
In the Graph, bind each button's click once:
- Select the button in the Designer and click + next to On Clicked in its Details › Events, or
- In Event Construct, drag the button's variable and use Assign On Clicked.
Bind clicks in Construct, never in On Activated
Assign adds a handler every time it runs. On Activated runs every time the screen is reopened, so the third visit fires your click three times.
CommonUI reuses closed screens
A closed screen is pooled and handed back the next time it is pushed. Anything that must start fresh on each visit (a selection, a flag, a "done" bool) must be reset in On Activated, not in Construct or as a variable default.
4. Add the entry to the menu¶
- Open Edit › Project Settings, and under Game choose XenoCore UI.
-
Under Menu › Menu Entries, click +. Fill in the new entry:
Field Value What it does Id BestiaryA stable name. Another list can replace this entry by using the same Id Label BestiaryThe button's text Description Monsters you have met.The line along the bottom of the menu while the entry is highlighted Screen W_BestiaryThe screen pushed when the button is pressed Layer leave empty Empty means UI.Layer.MenuSort Order 650Lower sorts higher. The shipped entries are 100 apart Visible If leave empty A condition class; empty means always shown Starts Group unticked Leaves a gap above the entry (Menu Group Gap), as above Save Hidden unticked Leaves the entry out without deleting it -
Close Project Settings. The change is saved to
Config/DefaultGame.ini.
The shipped entries, and their Sort Order:
| Sort | Id | Label | Opens |
|---|---|---|---|
| 100 | Equipment |
Equipment | Screen W_XenoCore_EquipmentScreen |
| 200 | Arts |
Arts | Action Open Arts Screen |
| 300 | Skills |
Skills | Action Open Skills Screen |
| 400 | Collectables |
Collectables | Screen W_XenoCore_ItemMenu |
| 500 | Party |
Party | Screen W_XenoCore_PartyManager |
| 600 | QuestLog |
Quest Log | Action Open Quest Log |
| 700 | ChangeTime |
Change Time | Screen XenoCoreChangeTimeScreen |
| 800 | Save |
Save | Action Open Save Screen (Starts Group: the system entries follow a gap) |
| 900 | Load |
Load | Action Open Load Screen |
| 1000 | Tutorials |
Tutorials | Screen XenoCoreTutorialLogBase |
| 1100 | Settings |
Settings | Screen Lyra's W_LyraSettingScreen |
| 1200 | ReturnToTitle |
Quit to Title | Action Return To Title (asks first if there is unsaved progress) |
| 1300 | QuitGame |
Quit Game | Action Quit Game (asks first if there is unsaved progress) |
So 650 puts Bestiary between Quest Log and Change Time. Two entries with the same Sort Order keep the
order they are listed in.
When the entry is pressed, the screen is pushed onto its layer and focus goes to the screen's Get Desired Focus Target, the same as the shipped screens. Pushing on the menu layer stacks your screen on top of the menu, so Back returns to the menu without any extra wiring.
Don't push onto UI.Layer.Modal or the Game layer
UI.Layer.Modal is for Lyra's full-screen dialogs and does not stack under the menu.
The game layer holds the HUD, and a screen there never takes Menu input mode.
Fails silently: an Id that is already taken
Entries are merged by Id. An entry whose Id matches a shipped one (for example Save)
replaces that entry rather than adding a new button. Give each of your entries its own Id.
Thirteen entries fill the menu at 1080p
The shipped thirteen entries, with their padding and the group gap, just fit a 1080-pixel-high screen without scrolling. Before adding a fourteenth, lower Menu Button Padding or Menu Group Gap, or hide an entry you do not need, and check the bottom entry is still on screen.
5. Optional: keep your entries in your own asset¶
Editing Menu Entries changes the project's DefaultGame.ini. To keep your entries in your own
content instead (for example inside your own Game Feature plugin):
- In
Content/MyGame/UI, create a Data Asset of classXenoCoreMenuEntrySet. Name itMES_MyGame_Menu. - Add your entries under Entries, filled in as in step 4.
- In XenoCore UI, add the asset to Menu › Additional Menu Entry Sets.
Sets are merged after Menu Entries, in list order. An entry with a matching Id replaces the earlier one where it stands, so a set can also reword a shipped entry, or tick Hidden on it to remove it. An entry with no Id is always added.
6. Optional: do something other than open a screen¶
- Run something: create a Blueprint child of
XenoCoreMenuAction, override Run (you are given the Player and the Menu widget), and set it as the entry's Action. Action wins over Screen when both are set. The action runs on its class default object, so keep no state on it. - Show it only sometimes: create a Blueprint child of
XenoCoreMenuCondition, override Is Visible (given the Player), and set it as Visible If. It is asked each time the menu opens. -
A different button: set Menu › Menu Button Class to your own CommonUI button. Implement the
XenoCoreMenuButtoninterface on it to receive the whole entry (label and Icon) through Set Menu Entry; otherwise the shell calls its Set Button Text function. The default isXenoCoreMenuEntryButton, a Party Tactics-style row; Lyra'sW_LyraMenuButtonalso works. Menu Button Padding sets the space around each button (default 4 px below). -
A menu of your own layout: call Build Menu In Panel (or Build Menu with a panel reference) from your widget's Construct to fill a panel with the same buttons, or read Get Menu Entries and call Activate Menu Entry yourself.
Test it¶
- Open
Maps/L_XenoCore_Gym, press Play, open the menu (pad Y, keyboard I). - Bestiary is in the list between Quest Log and Change Time. Select it with the pad; its description shows along the bottom of the menu.
- The screen opens with your first button selected. Move with the stick or arrow keys.
- Press A on a button and its click runs once.
- Press Jump: the character does not jump.
- Press B (keyboard Escape). You are back on the menu. Close the menu and move normally.
- Open Bestiary again and click once: it still fires once.
Keyboard
Keyboard Enter or Space presses the focused button too. See Controls.
If it doesn't work¶
Back does nothing
Is Back Handler is not ticked on W_Bestiary.
The character jumps or moves while the screen is open
Input Config is left at Default. Set it to Menu.
The screen opens but nothing is selected on a gamepad
Override Get Desired Focus Target.
A click fires twice after reopening
You bound it in On Activated. Move the binding to Event Construct.
Bestiary is not in the menu
Check Hidden is unticked, Visible If is empty or returns true, and no other entry
(in Menu Entries or an entry set) has the same Id. If the button is there but does
nothing, the Output Log names the entry (LogXenoMenu): "has neither an Action nor a Screen",
or "did not load".
Fails silently in a packaged build: the entry does nothing
A screen or action named only in settings is not found by the cooker. Put your UI under a folder that is always cooked, or add its folder to Additional Asset Directories to Cook. See Restyle the menus.
More in UI and input troubleshooting.
Going further¶
- Add a confirmation popup to ask Yes/No inside your screen.
- Add an input action with a prompt to show a footer glyph for a screen action.
- Add a mechanic by subclassing: the C++ screen bases, for a programmer, if your screen needs logic a Blueprint graph struggles with.