Skip to content

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

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_ToggleMenu pushes 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

  1. In MyGame/UI, right-click and choose User Interface › Widget Blueprint. Pick LyraActivatableWidget as the parent class. Name it W_Bestiary.
  2. Open it. In the Designer, build the layout: a background, a title, and a vertical box of buttons or a list. Use W_XenoCore_MenuButton or W_XenoCore_TabButton from UserInterface/Foundation so it matches the rest of the menus.
  3. 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:

  1. Select the button in the Designer and click + next to On Clicked in its Details › Events, or
  2. 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

  1. Open Edit › Project Settings, and under Game choose XenoCore UI.
  2. Under Menu › Menu Entries, click +. Fill in the new entry:

    Field Value What it does
    Id Bestiary A stable name. Another list can replace this entry by using the same Id
    Label Bestiary The button's text
    Description Monsters you have met. The line along the bottom of the menu while the entry is highlighted
    Screen W_Bestiary The screen pushed when the button is pressed
    Layer leave empty Empty means UI.Layer.Menu
    Sort Order 650 Lower 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
  3. 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):

  1. In Content/MyGame/UI, create a Data Asset of class XenoCoreMenuEntrySet. Name it MES_MyGame_Menu.
  2. Add your entries under Entries, filled in as in step 4.
  3. 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 XenoCoreMenuButton interface 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 is XenoCoreMenuEntryButton, a Party Tactics-style row; Lyra's W_LyraMenuButton also 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

  1. Open Maps/L_XenoCore_Gym, press Play, open the menu (pad Y, keyboard I).
  2. 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.
  3. The screen opens with your first button selected. Move with the stick or arrow keys.
  4. Press A on a button and its click runs once.
  5. Press Jump: the character does not jump.
  6. Press B (keyboard Escape). You are back on the menu. Close the menu and move normally.
  7. 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