Enhanced Input System

This page is a work in progress.

Unreal Engine 5 introduces the Enhanced Input System which assits games in handling user input. Satisfactory extends this system to allow for automatic detection of mod keybinds in the game’s settings menu and automatic integration with the base game’s contexts.

The Unreal Engine documentation provides a good overview of the system, and you should read that page first, but there are a few extra steps required to interface correctly with Satisfactory’s version of the system.

Introduction

The Enhanced Input System differs from Unreal’s previous input system in that it is data-driven. Actions that the player can take are defined in Input Action data assets, and Input Mapping Context data assets group actions into situations with information on how they are triggered.

During gameplay, multiple Input Mapping Contexts are dynamically added or removed to process the player’s input and determine which Input Actions to fire based on actual keyboard and mouse input.

For example, the base game has controls for walking around and driving a train. Walking forwards and throttling the speed up on a train are generally both bound to the W key, so how does the game know what to do when the player presses W? The Enhanced Input System decides by looking at which Mapping Contexts are in play and what their priorities are. Separate Input Actions exist for walking as a player and throttling trains. The walking action is included in Mapping Context MC_PlayerMovement and the train throttle action is included in MC_Trains. The MC_Trains context is only applied to the player when they enter a train and is removed when they exit it.

When the player presses W, the Enhanced Input System checks the contexts in order of priority. If MC_Trains is active, the throttle action is fired, but if MC_PlayerMovement is active, the walking action is fired instead.

In the real game, there are many more contexts and actions at play, and it could be the case that the player movement context is actually removed while the player is in a vehicle. Regardless, hopefully this example gives you an idea of how the system works.

Organization

The existing structure of the Enhanced Input System lends itself well to storing Input Action assets in folders with their contexts. Base-game input actions and contexts are mostly stored in /FactoryGame/Inputs/ and /FactoryGame/Interface/UI/Inputs/. You can take inspiration on how your mod should organize its contexts from looking at those folders.

Input Actions

Input Action assets can be created in Blueprint via Create Advanced Asset > Input > Input Action in a content browser window. The Unreal documentation explains how to create them in C++.

Note that Input Actions do not have any definition about what key they are bound to - that information is set by the context they are in.

The "Action Description" text will appear to the user when they hover over the binding in the settings menu. "Consume Input" should be set to false unless you have a good reason to set it otherwise, since this means it will prevent any other bindings on this key from taking effect if the relevant Context the Action is in has higher priority.

Standard 'key presses' usually have a Value Type of Digital (bool) and have Triggers on Pressed and Released with a threshold of 0.5. Look at the base game’s input actions for other examples.

Input Mapping Contexts

Satisfactory adds additional functionality to Input Mapping Contexts that are used instead of some of Unreal’s fields.

Your Input Mapping Contexts should be of type FGInputMappingContext and NOT InputMappingContext. Note that in order to create one in Blueprint you must use Create Advanced Asset > Miscellaneous > Data Asset and search for FGInputMappingContext. Alternatively, you can copy an existing mapping context and blank it out for your own use, for example, you can copy one from the base game or from ExampleMod. The Unreal documentation explains how to create them in C++, but you need to adapt it to use the aforementioned Satisfactory-specific parent class.

Again, make sure that you are using FGInputMappingContext and NOT InputMappingContext!

Input Mapping Contexts should have all of the relevant Input Actions provided in the Mapping field, which is also where you are allowed to assign default key bindings for each action. For each Input Action you list, you must:

  • Enable "Is Player Mappable" (unless you don’t want it to be edited by the user, but 99% of the time you do)

  • Under "Player Mappable Options":

  • Provide a "Name", otherwise the bind will not be user editable and attempting to look up its display name will fail.

  • Set a "Display Name", which is the text that will appear to users when they view or search for the action in the settings menu.

  • Note: the "Display Category" field here is ignored by Satisfactory.

Satisfactory adds its own custom fields to Input Mapping Contexts that appear in the "Mapping" category.

  • The "Display Name" is the name of the section heading the Actions will be grouped under in the settings menu. This should usually be your mod’s user friendly name.

  • "Menu Priority" presumably allows for reordering categories, but is 0.0 for all base-game Contexts and has not been tested.

  • "Parent Context" is described in detail below.

Once an input action is added to a Context and the Context is properly formatted with a Display Name it will appear in the settings menu. You do not need to register your actions nor contexts anywhere else.

If your mod has relatively few key binds you will probably only have one or two contexts for your mod: one that will be applied when the player is moving about as normal and one for when they are in the user interface.

Parent Context

Parent Contexts are a feature provided by Satisfactory for usage by mods. We can’t add our own Actions to the existing Contexts that are shipped with the base game, but we can instead specify a Parent Context for our own Contexts. When the Context we specified as a parent is applied by the game, our context will also be applied, and vice versa when the parent context is removed.

If you want the context to be available:

  • All the time, even when in interaction widgets such as the inventory, use MC_PlayerController. Be careful with this! If you use this context, you should not have Consume Input enabled for the listed actions because it will block other keybinds from working.

  • Whenever the player can interact with buildings and their held equipment, use MC_PlayerActions

  • Whenever the player is in a building interaction widget, use MC_UserInterfaceBase

  • Whenever the player is in their inventory, use MC_Window

  • In a special condition, such as when the build gun is open in a certain mode, look at the existing contexts to see which is the best choice

  • In an even more specific condition, then you may wish to use manual mapping context registration instead.

Responding to Input Actions in Code

It is suggested that your code respond to input actions in either an actor (such as a Subsystems), a widget, or a custom actor component on the player character or controller attached via Simple Construction Script (SCS) Hooks.

On some actors, such as Subsystems, you may need to call Player Controller instance > EnableInput for them to process user input.

Blueprint

In order to create the Blueprint event node for responding to an input action, you may have to turn off "Context Sensitive" in the blueprint action selector for it to appear in the search results.

Type the name of the input action to create the Event node for responding to it.

C++

The Unreal documentation explains how to handle responding to Input Actions in C++, except it assumes we have control over the player controller, which modders do not. Thankfully Coffee Stain has created a delegate AFGCharacterPlayer::OnPlayerInputInitialized which mods can bind to. Each time SetupPlayerInputComponent is called the delegate will be called, giving you a chance to register your custom inputs.

Gameplay Tags

If you have a large number of input actions, it may be helpful to use Gameplay Tags to reference them.

To use them, create a custom GameInstanceModule class in C++ containing a TMap<FGameplayTag, UInputAction*> and use your new class as the parent of your "real" GameInstanceModule blueprint asset:

// Includes left out

UCLASS(Blueprintable)
class MYMOD_API UMyModGameInstanceModule : public UGameInstanceModule {
    GENERATED_BODY()
public:
    UPROPERTY(EditDefaultsOnly, Category = "Advanced | Input")
    TMap<UInputAction*, FGameplayTag> InputActionTagBindings;
};

Make sure to add calls to super functions if you add any additional blueprint code in this asset. Then, to use the bindings, you would do as follows:

UGameInstance* GameInstance = GetWorld()->GetGameInstance(); // May vary depending on the class you're using this from
UGameInstanceModuleManager* GameInstanceModuleManager = GameInstance->GetSubsystem<UGameInstanceModuleManager>();
UMyModGameInstanceModule* MyModGameInstanceModule = Cast<UMyModGameInstanceModule>(GameInstanceModuleManager ->FindModule(TEXT("MyMod")));
EnhancedInputComponent->BindAction(MyModGameInstanceModule->InputActionTagBindings[FGameplayTag::RequestGameplayTag(TEXT("Tag.Name"))], ETriggerEvent::Triggered, this, &Class::Function);

Reading Input Action Information

In order to retrieve the name(s) of the key(s) bound to an action for display to the user, call the FGInputLibrary function Get Input Action Name as Text on a Player Controller instance passing in the Name given to the action in its Mapping Context. This will automatically handle multi-key binds and display them in a combined format like Left Alt + Q. Note that if the mapping context is not registered at call time you will get back UNKNOWN_KEY().

If you need to retrieve the exact key structures involved in an input action, call the FGInputLibrary function Get Current Mapping for Action on a Player Controller instance passing in the Name given to the action in its Mapping Context. Be sure to check the boolean return value for success/failure status.

Manual Mapping Context Registration

The primary method of registering your Contexts is usually via the Parent Context system, however, is is possible to manually manage the registration of your context.

You can get a reference to the EnhancedInputLocalPlayerSubsystem via a Get node on a player controller instance. From there, you can call AFGPlayerController::SetMappingContextBound to control when your context is applied.

You should not use Add Mapping Context or Remove Mapping Context because those do not interact with Satisfactory’s parent context system, meaning your contexts cannot be extended as described in the[Parent Context section.

Chorded Actions

TODO ask D4rk, required to do Ctrl/Alt/Shift + key

Handling Input in User Interfaces

Sometimes a modded user interface requires extra Actions to be bound while it is active. An example of this is the Workbench UI in the base game, which binds the spacebar to holding down the craft button.

Create a Context and Actions for use when the widget is open. As long as your modded interface widget extends Widget Usable Base, context management is handled for you. Simply specify the MC_UserInterfaceBase context and your own custom context in the widget’s mInputMappingContexts field and enable mInputGateDelayOpen.

An example of this can be found in Widget_InputExample in the ExampleMod.

If you override the Destruct method in your widget, make sure to call the parent implementation or the widget will not properly unregister the input contexts!

If your interface does not extend Widget Usable base you will have to manually manage context registration, handle your input in a subsystem instead, or have the context always registered via parent context MC_PlayerController and decide based on additional conditions whether to handle the input or not.

Debugging

Unfortunately the very useful showdebug enhancedinput console command described in the Unreal documentation is not available in shipping builds. However, the command showdebug INPUT is available and displays a reduced set of information, such as which mapping contexts are currently bound.