Skewed settings menu - Gamepad support
5/16/2024
Kaloyan Geshev
Many modern games incorporate gamepad compatibility, enabling players to engage with the game, user interface (UI), and menus seamlessly.
Gameface supports the integration of gamepads into your UI. We provide a dedicated library for Gameface, known as the Interaction Manager . This library is capable of managing various interactions with the UI, including keyboard inputs, gamepad commands, touch gestures, and more.
Showcase Overview
To illustrate the process of integrating gamepad support into your UI, we will expand upon the Skewed settings menu sample.
We will utilize the interaction manager to handle gamepad input and add our own logic to handle various actions. Notable additions in this sample include:
- Navigating through menus using gamepad controls.
- Selecting different options and adjusting their values via the gamepad.
- Introduction of a new page for remapping in-game gamepad controls using the gamepad itself.
- Introduction of a new page displaying a graphic schema of the gamepad and providing tips for each button’s function in the game.
Source location and sample preview
You can find the complete sample source within the ${Gameface package}/Samples/uiresources/UITutorials/SkewedSettingsMenu
directory.
To enable gamepad controls for the sample, follow these steps:
- Connect a gamepad to your PC.
- Build, run the sample, and play around using the gamepad. You can build and preview the sample in various ways:
- Run
npm run watch
. This command starts a development server with a live reload. After running thewatch
command, launch the Player using thePlayer.bat
file and provide the followingurl
argument:"--url=http://localhost:9000"
. - Run
npm run build
. This command generates a production (minified) build in thedist
folder. Then you can run theindex.html
from thedist
folder in the Player. - Run
npm run build:dev
. This command creates a development build (non-minified) in thedist
folder. Then you can run theindex.html
from thedist
folder in the Player.
- Run
Sample in details
Used assets
To showcase the gamepad buttons, we’ve utilized assets from the following free Figma file .
Interaction manager
To begin, we must install the interaction manager by running npm i coherent-gameface-interaction-manager
within the ${Gameface package}/Samples/uiresources/UITutorials/SkewedSettingsMenu directory
.
Define the gamepad controls mapping
The mapping for gamepad controls within the game is established through the gamepad-controls-model.js
file. This file follows a structured format:
In this structure:
actionName
specifies the function of the control.control.alias
is utilized to designate the control alias, as defined in the interaction manager gamepad documentation.control.icon
provides the path to the control icon within theassets/gamepad-controls
directory.
Additionally, within this file, a flag named gamepadControls
is defined. This flag toggles the gamepad support within the sample when it is built using relevant commands. When set to true
, the sample will be built with gamepad support enabled. Moreover, the Controls/Keyboard mappings
page will be hidden, and in its place, the Controls/Gamepad
mappings and Controls/Gamepad schema
pages will be visible.
Webpack configuration change
To enable the utilization of gamepad icons introduced in gamepad-controls-model.js
, we must modify the webpack configuration slightly and integrate the copy-webpack-plugin
. Follow these steps:
- Install the
copy-webpack-plugin
by running:npm i --save-dev copy-webpack-plugin
- Update the webpack configuration:
Enabling the gamepad in Gameface
Once the setup is complete, we can enable the gamepad in Gameface when gamepadControls
variable is set to true
.
src/index.js
Disabling UI elements
When controlling the menu with or without a gamepad, it’s necessary to provide an option to disable parts of the UI that aren’t relevant. For instance, the Keyboard mappings
submenu isn’t needed when a gamepad is connected. Instead, we require the ability to modify gamepad mappings, necessitating the enabling of two different submenus: Gamepad mappings
and Gamepad schema
. The same applies when the menu is controlled without a gamepad - in that case, we enable Keyboard mappings
and disable the gamepad-related submenus.
To achieve this, upon initialization of the settings-page
, we identify the elements to be hidden and remove them from the Document Object Model (DOM)
using the .remove()
method.
src/pages/settings-page/script.js
Navigating options using gamepad controls
To facilitate option navigation within each page, a custom method is required to handle this functionality and trigger it upon pressing specific buttons on the gamepad. In this example, we’ll configure this operation for the pad-up
and pad-down
buttons (or the up/down arrows of the gamepad).
First, we need to implement an action in the interaction manager to manage the gamepad’s pad-up/down
buttons. This is accomplished within the toggleGamepadActions
method of the settings-page
component.
When selectOption is called with 1
or -1
, it signifies the direction in which the next option should be selected. -1
indicates upward movement, while 1
indicates downward movement.
Using the native nextElementSibling
and previousElementSibling
, we can obtain the previous and next elements to be selected. Since the options are arranged vertically within a gameface-scrollable-container
, it’s possible to select an option that isn’t visible. Hence, scrolling the scrollable container is necessary to make the option visible. This is achieved by calling the scrollToElement
method, which performs the following:
Adjusting option values using a gamepad
When navigating through menu options, it becomes necessary to facilitate the modification of their values using a gamepad. To achieve this functionality, the pad-left
and pad-right
actions (or the left/right arrows on the gamepad) are utilized within the provided sample.
Each option within the menu may consist of various components such as rangeslider, stepper, or radio buttons, each requiring a different approach for value adjustment when the gamepad arrows are pressed.
To begin, an interaction manager action is added to handle the gamepad’s pad-left/right
buttons. This is implemented within the toggleGamepadActions
method of the settings-page
component.
The updateOptionValue
method performs the following actions:
- The stepper component provides methods
stepper.next
andstepper.prev
for adjusting its value. - To adjust the value of a rangeslider, the
rangeslider.value += 1; or -= 1
syntax is utilized. - The radio group offers
setCheckedToNextItem
andsetCheckedToPreviousItem
methods, that accept the currently selected radio button as an argument. It can be retrieved throughradiogroup.querySelector('[aria-checked="true"]')
.
Navigating settings menus and submenus using a gamepad
To enable interaction with all menus using a gamepad involves implementing an interaction manager action to handle gamepad buttons such as left/right-shoulder-bottom
and left/right-shoulder
. The implementation is located within the toggleGamepadActions
method of the settings-page
component.
The selectMainTab
and selectSubTab
methods perform the following tasks:
Utilizing the methods selectTab
, getPrevTab
, and getNextTab
from the gameface-tabs
component, we can efficiently manipulate the main and sub tabs programmatically via JavaScript.
Creating a gamepad mappings page
In the ${Gameface package}/Samples/uiresources/UITutorials/SkewedSettingsMenu/src/pages/settings/controls
directory, we’ve implemented a page named Gamepad mappings
. This page allows users to remap their gamepad controls for use within the game. Navigation through the options is facilitated by using the gamepad’s pad-up
and pad-down
controls. Upon selecting an option by pressing the X
button, users can then press any button on the gamepad to replace the existing one.
Here’s how we accomplished this:
- Dynamically generated options based on the model outlined in
gamepad-controls-model.js
. - Developed a
gamepad-select
component, visually indicating when a key is being changed. - Implemented logic to handle gamepad input for remapping keys.
The dynamic options are generated by the setOptions
method within the gamepad-mapping
component, located in /SkewedSettingsMenu/src/pages/settings/controls/gamepad/script.js
.
To enable control changes, we’ve added an action for when the gamepad’s X
button is pressed. This interaction is managed through the interaction manager:
In the selectOption
method, the selected control becomes active, awaiting input from another button on the gamepad to replace it.
The toggleButtonsGamepadActions
method is responsible for adding actions for all buttons on the gamepad defined in gamepad-controls-model.js
. Its purpose is to detect the next clicked button from the gamepad to be chosen as the new one.
Additionally, when a new button is selected, we need to deactivate the actions from the interaction manager to prevent their execution when unnecessary.
Once a new gamepad key is chosen, the old control is swapped with the new one both in the UI and in the model.
Creating the gamepad scheme page
In Gameface, generating a detailed breakdown of each gamepad key’s function upon clicking is easily achievable with a small amount of JavaScript and SVG.
You can find the page showcasing this breakdown at /SkewedSettingsMenu/src/pages/settings/controls/gamepad-schema
.
SVG gamepad preparation
To create this breakdown, we first need to craft the gamepad in SVG format. We utilized Figma for this task because it allows direct export of textures into SVG while preserving element IDs. This feature is incredibly powerful as it enables dynamic control changes for each gamepad key.
You can directly access our design document on Figma here .
Once you’ve opened the file, select the frame, and navigate to Design
-> Export
. Change the export mode to SVG
, then from the three dots, ensure that Include "id"
attribute is checked and Outline text
is unchecked. Afterward, export the frame into SVG. You can preview the SVG file’s content into /SkewedSettingsMenu/src/pages/settings/controls/gamepad-schema/template.html
.
Following this step, some modifications are necessary to ensure everything functions properly:
- Add
x
andy
attributes to thetext
elements to position them correctly within theSVG
in Gameface. This task is easily accomplished using the inspector in Gameface. - Change the
fill
to white. - Remove the
tspan
elements as they are unnecessary. - Remove the
xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="12" letter-spacing="0em"
attributes from the text, as they are redundant. - Add the appropriate
text-anchor
attribute (middle
,start
, orend
) to each text element based on its position. This ensures that when new text is dynamically added, the space expands in the correct direction relative to the text element.
Adding the controls
After obtaining the SVG schema, the next step involves dynamically integrating controls into the SVG text elements associated with gamepad keys.
In the setControls
function, the script locates the SVG text
element corresponding to each gamepad control from the model and updates its textContent
with the relevant action name.
Furthermore, to ensure that the schema updates dynamically when the gamepad mappings change, a custom event listener is added via document.addEventListener('gamepad-controls-changed', this.setControls)
. This listener triggers the setControls
function whenever the gamepad controls model is altered from the Gamepad mappings
page.