Adding Difficulty Settings

Overview

In this short tutorial we'll show you how to add a simple selector to the Singleplayer menu which will allow users to specify the game difficulty.

Find The Correct Graph

Step 1 is of course to open up the Sandbox Editor and open up flowgraph to access the UIActions.

The graph you want is UIActions -> MM_PagesMain -> MM_SinglePlayer. This is the screen that is shown when clicking on the "Singleplayer" option from the main menu.


MM_SinglePlayer UIActions flowgraph, as of version 3.6.6.

Adding Switches

Now we want to add some switches that the user will interact with.

First start by adding a UI:Functions:MainMenu:AddSwitch node.

We also know that we're going to have 5 difficulty options available, so add 5 UI:Functions:MainMenu:AddSwitchOption nodes.

As the names indicate, a SwitchOption is an 'option' that is given for use with the Switch node, so you have one switch and five options for that switch.

The recommended layout is to put your Switch at the top and then in a vertical fashion, list your Options below the switch.

Just slot your new elements in the chain where it makes logical sense, something like this:

Always keep in mind the sequential loading approach with UIActions. You almost never want to fire multiple logic sequences at the same time which can provide unpredictable results so wire up your logic sequentially rather than in parallel.

Node Information

By default, the nodes are of course empty, but the data you need to enter into them is very simple once you understand what it's after.

  • Call is simply a trigger for the node. As you can see in the above screenshot, Call -> OnCall -> Call to string nodes together in sequential fashion.
  • InstanceID by default is set to -1 (lazy init) and it's usually okay to leave it like this unless you have special circumstances where it needs to be isolated to its own instance.
  • Caption is the text that will be displayed in the menu. This should be done with localization strings but it's also okay to use plain text.
  • Tooltip is the accompanying text that will be displayed at the bottom of the screen to give a more in-depth description of the selected option. In localization, you may come across "TT" at the end of strings, which indicates "Tool Tip".
  • ID is very important and will be used to communicate across the flowgraph. It can be called whatever you want but it should be unique to prevent incorrect/duplicated results. It's recommended to copy/paste to prevent any typos.
  • Value is used to define behavior from the selected options. This can be strings, integers or floats.

Take a look at the screenshot below to see how they've been filled out for this setup:

If your game uses multiple languages/localization, you should take advantage of the Localization System and use localization strings for the text input fields.

The ID "ID_Difficulty" was chosen as it is the most logical string to use for this setup. Note that the SwitchOptions need the same ID as the Switch as this is how they're logically connected.

Then simply fill in the Captions (done in plain text for this example) and assign a logical value to each, easy=1, normal=2, etc.

Event Handling

So far we've added our switches, but they don't actually do anything yet. Now we need to hook up the events handling part so that things will actually happen depending on which option is selected.

For this example, we're going to use a simple CVar change via the Debug:ConsoleVariable node.

To retrieve the data from the Switches we'll use the UI:Events:MainMenu:OnControlChanged node.

Node Information

  • InstanceID by default is set to -1 (lazy init) and it's usually okay to leave it like this unless you have special circumstances where it needs to be isolated to its own instance.
  • Port is the method you wish to receive the data. By default it is set to "<TriggerAlways>" which means the data is always received on any trigger from any other node in this UIAction. However, we only want to receive the data when this specific Switch is changed, so we'll listen on the "Id" port.
  • Idx is the ID we defined earlier in order to connect the SwitchOptions to the Switch, and now we're also connecting this OnControlChanged node via the same ID.
  • onEvent is the trigger for output data being sent and is fired when the conditions of the "Port" are met and data is sent from the SwitchOption. This is being used to 'Set' the ConsoleVariable node.
  • InstanceID can be used if you're using a specific ID setup which isn't the case here.
  • Id will output the value of the Idx input if needed for your logic.
  • Value is the important output here as it will send the information about the currently selected SwitchOption that the Switch is using. So in our example, when the user selects "Normal" difficulty in the menu, the associated value from that SwitchOption node is '2' and that is the value which will be output here. With this information, you can then fire off logic based on these values. It just so happens that the '2' value can be directly used with the CVar and no additional deciphering of logic is required.

Readback Logic

If you've already tested this setup so far, you may have discovered one flaw. If the user were to select a difficulty option (say, "Extreme"), back out of the Singleplayer menu and then return to it, the difficulty option would have been forgotten about and displayed as "Easy" because that is the first SwitchOption the switch receives when building the page.

In order to correctly display the current difficulty setting we'll add in a simple setup to read back what the setting is, and then set the SwitchOption to this value.

At the end of the Setup logic (where we add our options in) add in the Game:DifficultyLevel node, Math:SetNumber nodes and UI:Functions:MainMenu:SetControlVal node as pictured below.

When the UIAction is finished building the initial options it will then retrieve the current difficulty level. With that information we will assign values accordingly (to match our SwitchOption nodes) with the Math nodes.

Finally, with the current difficulty information and its associated numbers, we'll use the SetControlVal node to change the SwitchOption node so that it correctly displays the current difficulty level.

Results

Now, you should have something like this in your menu:

You may also notice each time you select a different config it is confirmed through console:

<18:50:14> CGame: Loading singleplayer difficulty config 'difficulty/normal.cfg'
<18:50:14> Loading Config file difficulty/normal.cfg (gamesdk\difficulty\normal.cfg)
<18:50:15> CGame: Loading singleplayer difficulty config 'difficulty/hard.cfg'
<18:50:15> Loading Config file difficulty/hard.cfg (gamesdk\difficulty\hard.cfg)