[UE5][Intermediate] Integrating CommonUI and Enhanced Input into Gameplay

[UE5][Intermediate] Integrating CommonUI and Enhanced Input into Gameplay

What problem does it solve?

Common UI is a very promising technology for coping with biggest issues of UMG. It's beta state makes it prone to changes that aren't reflected in documentation and tutorials aren't up to date. This article shows how to set it up for UE 5.5 in a real gameplay scenario, where focus needs to seemlessly switch to UI for both Mouse + Keyboard and Gamepad.

Background

During my 10+ year career as Unreal Engine Programmer I developed UI for several projects and there was always an issue with Gamepad support in:

  • Moving focus between widgets
  • Integrating Gamepad driven Gameplay with UI
  • Seemless switching between Mouse + Keyboard and Gamepad while maintaining focus

Those issues, along with many others like global styling, were to be solved in CommonUI plugin. It took over a week to make it work as intended and this article aims to save you the time.

Prerequisities

For simplification I assume you already have Unreal Engine 5.5 installed and know how to handle basic operations.

Additionally you need some background about how Common UI and Enhanced Input work. This article focuses solely on integrating them.

Research

Magnificent article on CommonUI setup

Epic's Official documentation - misses newest changes - especially that CommonUIActivatableWidget Activates itself on moving back on Stack. Manual Activation breaks the system!

Epic's Official YouTube Tutorial - massively outdated and only shows Main Menu, without Gameplay.

YouTube Tutorial #1 - doesn't include Gameplay under UI.

YouTube Tutorial #2 - uses SetInputMode nodes which breaks CommonUI.

Result

Final result of Common UI and Enhanced Input integration in Gameplay Context

Downloadable project

GitHub - ZimaXXX/CommonUITutorial: Tutorial of a proper CommonUI setup in a Gameplay environment
Tutorial of a proper CommonUI setup in a Gameplay environment - ZimaXXX/CommonUITutorial

Solution


TL;DR version

Main findings are:

  1. PushWidget Activates CommonActivatableWidget now, so you shouldn't call ActivateWidget yourself.
  2. SetInputMode is illegal to use with CommonUI and breaks it. You should use GetDesiredInputConfig instead.
  3. To properly handle the InputConfigs in Stack make sure that there's a default RootContentWidgetClass that handles setting it to Gameplay configuration.

Regular Version

Create project from Third Person template

Blueprint/C++ version doesn't matter

Add Clickable Gameplay

The most troublesome aspect is to have a clickable environment as part of the Gameplay, the one that was recently served by SetUserInputGameAndUI.

First let's crate an InputAction IA_GoTo and add it to Input Mapping IMC_Default with Left Mouse Button binding.

Next, create a PlayerController Blueprint called BP_TutPlayerController and configure it in Selected GameMode settings in World Settings.

Now open the created Player Controller and create method GetLocationUnderCursor. Configure it according to the image below.

In the Event Graph add IA_GoTo event and call SimpleMoveToLocation on data from GetLocationUnderCursor.

Finally we need to show the hidden Mouse Cursor in Class Defaults.

To make it work we lack NavMesh so let's add it NavMeshBoundsVolume from Place Actors Window and scale it up to contain whole level.

💡
If you can't see the green NavMesh press P with the Editor window focused.

Now you should be able to click on the level when playing to move to desired spot.

Create HUD Class

For managing Widget's HUD class is handy, so we create BP_TutHUD and configure it in the WorldSettings.

Enable CommonUI plugin

Go to Plugins Window and Enable CommonUI Plugin (beta).

Restart project afterwards.

Set Game Viewport Client Class

Open Project Settings, find GameViewportClientClass and set it to CommonGameViewportClass.

Don't restart project yet.

Enable Enhanced Input Support

Find EnableEnhancedInputSupport and Enable it.

Now you can restart the Editor.

💡
Setting GameViewportClientClass changes how input routing works.

Obtain input keys images

We need key images to be shown in UI ActionBars. I'm using Xelu's FREE Controller Prompts with a Creative Commons license. Drag and drop them into the project afterwards.

Create CommonUI configuration files

We need several assets to configure the CommonUI

  • TutCommonInputActionDataBase of class CommonUIInputActionDataTable
  • TutCommonInputBaseControllerData_GP of class CommonInputBaseControllerData
  • TutCommonInputBaseControllerData_MKB of class CommonInputBaseControllerData
  • TutCommonInputData of class CommonUIInputData

TutCommonInputActionDataBase contains info about all the inputs being used in CommonUI. Basic settings limit to providing keys and images of those keys. We need NavigateBack, NavigateForward, TabLeft and TabRight.

TutCommonInputBaseControllerData_GP (Gamepad) and TutCommonInputBaseControllerData_MKB (Mouse and Keyboard) contain details on how inputs look and behave per input device.

Configure them accordingly:

💡
Extremely important: set Gamepad Name and Gamepad Display Name to Generic.

TutCommonInputData requires EnhancedInput InputActions.

Create IA_UI_GenericAccept and IA_UI_GenericBack and configure them in IMC_Default:

Now configure the TutCommonInputData accordingly:

Fill in CommonUI Project Settings

Having all the pieces we can configure the CommonUI Project Settings.

First set the InputData to TutCommonInputData.

Next scroll down to Platform Input Windows and apply the settings:

💡
Again, very important to set Default Gamepad Name to Generic. Now the configs can find themselves.

Create Styles

Although not mandatory, it's nice to have a CommonUI project styled from the beginning. CommonUI introduced a well known from other front end enviroments global styles, so you can create a button style that is automatically applied to each CommonUIButton!

Create 3 different Styles:

  • CommonStyleBorderBase of type CommonBorderStyle
  • CommonStyleButtonBase of type CommonButtonStyle
  • CommonStyleTextBase of type CommonTextStyle

and configure them in Project Settings:

💡
Remember to configure the Styles appearence

UI architecure

Finally we can create the CommonUI Widgets. Basically, many of the regular UMG widget classes got their twin in CommonUI and those are now mandatory to be used.

The idea is to have a base WBP_HUD widget container which opens a WBP_IngameMenu. That widget has 3 buttons - Resume, Settings and Quit. Settings button opens a WBP_Settings widget which allows Tab switching. Tabs wll become consecutively: Video, Audio and Gameplay.

Creating widgets

Buttons

First we need a basic button. Let's create a WBP_ButtonBase of class CommonButtonBase.

Now create children of that WBP_ButtonBase: WBP_MenuButton and WBP_TabButton.

Finally we need an WBP_ActionBarButton of type CommonBoundActionButton.

WBP_ButtonBase will serve as a template for more specified buttons. It requires a CommonText and a CommonActionWidget placed in an Overlay.

💡
CommonText needs to be called ButtonText and CommonActionWidget needs to be called InputActionWidget so Widgets automatic Bindings work.

If everything is set up properly the Bind Widgets Window should show a green check mark.

You can now set a DesignTimeKey setting to check if TutCommonInputData is properly bound.

💡
If Button Image is not displayed first check if ProjectSetting's Input Data variable is set. If it's ok be sure you provided Image for the desired Button in that DataTable. There can be also issue with the Soft pointer of Input Data if you moved the files around, so set it up again.

in the EventGraph we expose the variable Text and apply the text on certain events to the ButtonText.

WBP_MenuButton is going to be used just as a higher level of abstraction of ButtonBase (you should never use Base classes directly as they suggest being abstract).

WBP_TabButton will be used for WBP_Settings widget and we don't want to have the InputActionWidget visible so we hide it:

WBP_ActionBarButton is a different story and will be used in ActionBar of the WBP_IngameMenu which shows all available inputs and their actions.

We configure it similarly to WBP_ButtonBase but now CommonText needs to be named Text_ActionName.

I used ScaleBox as well to better fit the widgets inside ActionBar container.

In the Bind Widgets tab there should be 2 bindings approved.

HUD

As described in the UI Architecture section we first create a global conainer WBP_HUD. It needs to be of type CommonUserWidget.

WBP_HUD is a host for a Stack of widgets that will contain our menus.

Add a CommonActivatableWidgetStack and call it MenuStack.

💡
From my experience and reading the forums I found that there's a need to have a default RootContentWidgetClass for MenuStack that resets the UIInputConfig to Gameplay.

That's why we should create a WBP_StackStub of class CommonActivatableWidget and set it to RootContentWidgetClass.

Now in WBP_StackStub we need to overwrite GetDesiredInputConfig

Additionally a CommonBoundActionBar can be handy to display available action keys.

Hierarchy should look like this:

💡
Remember to set ActionButtonClass of BoundActionBar to WBP_ActionBarButton

Now go to BP_TutHUD and make it show up on BeginPlay.

Ingame Menu

Let's create WBP_IngameMenu and WBP_Settings of type CommonActivatableWidget.

For WBP_IngameMenu we need 3 WBP_MenuButtons to handle our Resume, Settings and Quit features.

Most importantly you need to set the IsBackHandler and IsBackHandlerDisplayedInActionBar to true.

In the EventGraph we set the OnClicked events of the Buttons.

Additionally, we need to tell the system which Button is our default one by setting the Focus OnActivated.

PushWidget is a method for managing Stack on WBP_HUDs MenuStack. Let's add a configuration variable ParentMenuStack for pushing new widget.

Until around version UE 5.5 Pushed Widgets required calling ActivateWidget, which made the widget to show up and handle inputs. After a lengthy debugging session I found that those PushWidget effectively Activates widget as well (even wighout AutoActivation setting). Calling it twice can break the initialization logic!

💡
Since UE 5.5 PushWidget Activates Widget internally. Calling it manually afterwards can break the initialization logic!

2 methods are required to be overridden.

GetDesiredFocusTarget should return the ReturnButton.

GetDesiredInputConfig serves as a replacement to former SetInputMode (which is illegal to use with CommonUI).

💡
SetInputMode methods are illegal to use with CommonUI! Use GetDesiredInputConfig on CommonActivatableWidget.

First test run

To finally test if we made CommonUI to work we need to handle EnhancedInput InputAction.

Create IA_OpenMenu, add it to IMC_Default and configure Gamepad's StartButton and Escape on keyboard.

Using Escape in Editor is troublesome as it closes the PIE (Play In Editor) session. You can either remove the Escape binding from Editor Preferences or add a Chorded Action Trigger to use key combination. If so, create IA_ShiftChord, add it to IMC_Default and set it to Shift. Now add another key binding to IA_OpenMenu with Escape but now add Trigger Chorded Action and configure it to use IA_ShiftChord.

💡
This Chorded Action can be added to IA_UI_GenericBack as well.

Now we need a set of OpenIngameMenu calls. Let's start with WBP_HUD.

We added a branch to check if the stack is empty or set to default so we don't open menu again (it causes creation of new widget).

There's also a configuration just after pushing the widget so we can callback the MenuStack.

💡
Don't store Pushed Widget in a Variable. It's state isn't guaranteed and it can become overwritten with a new widget instance or set to null anytime by the Stack implementation.

Next step is routing the code by BP_TutHUD.

Finally we can serve the IA_OpenMenu in BP_TutPlayerController.

Now run the game and press any key you've bound the IA_OpenMenu to.

Adding WBP_Settings

To prove that widget stacking works we will now configure the WBP_Settings.

We need few additional widgets first:

  • WBP_TabWidget of type CommonActivatableWidget
  • WBP_TabListWidget of type CommonTabListWidgetBase

The WBP_TabWidget is where we can populate settings for the game. For our purposes it will only contain a CommonText with it's name.

And corresponding EventGraph with exposed TabText variable.

WBP_TabListWidget is for controlling which TabWidget is going to be viewed.

In the hierarchy we add a HorizontalBox named ButtonsContainer and we can fill it with some test Buttons to see how will they look when added dynamically.

The default settings need to be altered for listening to proper InputActions

💡
Remember to Enable AutoListenForInput

In the EventGraph we need to implement 2 methods - HandleTabCreation and HandleTabRemoval. Additionally we clean any Button tests from the widget on Construction.

In the WBP_Settings first we enable the Back Handlers.

The Hierarchy contains CommonActivatableWidgetSwitcher with 3 WBP_TabWidgets.

On top there's a HorizontalBox with 2 CommonActionWidgets for displaying keys and WBP_TabListWidget for WBP_TabWidget selection.

CommonActionWidgets need to have InputActions configured accordingly.

The EventGraph configuration is a little complicated.

We need to link WBP_TabListWidget with CommonActivatableWidgetSwitcher, register Tabs with WBP_TabWidgets and configure TabButtons.

Lastly we stretch the Buttons in ButtonsContainer as they were added dynamically.

We also have a single utility method for configuring the WBP_TabButtons.

Now we can Play the Game again and check if Settings are properly traversed.

This consluded the tutorial!

Conclusions

CommonUI is promising though difficult to configure plugin. Hopefully it will be more straightforward to configure when it leaves the Beta stage.

Possible improvements

There're a lot of other UI widgets to create that can cause trouble

  • Clickable widgets on HUD
  • Dialog boxes
  • Complicated drag and drop widgets like Inventory
💡
Let me know if you'd like me to create another content on those topics!