A complete replacement for Unreal's built-in Steam Input support, delivering zero-boilerplate integration with Enhanced Input and Slate UI systems. Built to solve real developer pain points with modern architecture.
Unreal's built-in Steam Input support was built for the old input system and never properly updated. Developers face manual key mapping, no Enhanced Input integration, broken Slate UI support, and extensive boilerplate code just to get basic functionality working.
A complete rewrite that automatically generates Enhanced Input keys from Steam actions, provides native Slate bindings, and gracefully handles all edge cases. Configure once, then forget—it just works.
Register your Steam actions once in project settings. The plugin automatically generates matching Enhanced Input keys and handles all the wiring.
Seamless support for gamepads, Steam Deck, keyboard/mouse, and any custom controller configuration through Steam's input system.
Actions marked as MouseInput automatically simulate raw mouse movement through Unreal's input pipeline—perfect for cursor controls and camera panning.
Rebindable Slate navigation actions. Developers can customize UI controls without code changes or rebuilds.
When Steam Input is disabled, all other input systems continue working normally. No interference, no broken controls.
Full support for Steam's action sets, enabling context-aware input schemes that adapt to different gameplay states.
The plugin integrates seamlessly with Unreal's input pipeline while maintaining complete compatibility with existing systems.
void FSteamInputController::SendControllerEvents()
{
if (!bControllerInitialized) return;
const double CurrentTime = FPlatformTime::Seconds();
InputHandle_t Controllers[STEAM_INPUT_MAX_COUNT];
const int ControllerCount = SteamInput()->GetConnectedControllers(Controllers);
for (int i = 0; i < ControllerCount; ++i)
{
const InputHandle_t Controller = Controllers[i];
FControllerState& State = ControllerStates[i];
// Activate the current action set for this controller
SteamInput()->ActivateActionSet(Controller, ActiveActionSet[i]);
// Process digital actions (buttons)
for (const auto DigitalAction : DigitalActions)
{
const InputDigitalActionData_t ActionData =
SteamInput()->GetDigitalActionData(Controller, DigitalAction.Key);
bool PreviousState = State.DigitalStatusMap.FindOrAdd(DigitalAction.Value);
if (!PreviousState && ActionData.bState)
{
// Button pressed
MessageHandler->OnControllerButtonPressed(
DigitalAction.Value, UserId, DeviceId, false);
}
else if (PreviousState && !ActionData.bState)
{
// Button released
MessageHandler->OnControllerButtonReleased(
DigitalAction.Value, UserId, DeviceId, false);
}
State.DigitalStatusMap.FindOrAdd(DigitalAction.Value) = ActionData.bState;
}
// Process analog actions (sticks, triggers, mouse input)
for (const auto AnalogAction : AnalogActions)
{
const InputAnalogActionData_t ActionData =
SteamInput()->GetAnalogActionData(Controller, AnalogAction.Key);
if (AnalogAction.Value.Value == EKeyType::MouseInput)
{
// Direct mouse input simulation
MessageHandler->OnRawMouseMove(ActionData.x, ActionData.y);
}
else
{
// Standard analog input (triggers, sticks)
MessageHandler->OnControllerAnalog(
AnalogAction.Value.Key, UserId, DeviceId, ActionData.x);
}
}
}
}
Setting up the plugin requires minimal configuration. Just define your Steam actions and their types:
void USteamInputSettings::GenerateKey(const FName ActionName, const EKeyType KeyType) const
{
switch (KeyType)
{
case EKeyType::Button:
{
const FKey Key{ActionName};
if (EKeys::GetKeyDetails(Key) == nullptr)
{
EKeys::AddKey({Key, Key.GetDisplayName(),
FKeyDetails::GamepadKey, MenuCategory});
}
}
break;
case EKeyType::Analog:
{
const FKey Key{ActionName};
EKeys::AddKey({Key, Key.GetDisplayName(),
FKeyDetails::GamepadKey | FKeyDetails::Axis1D, MenuCategory});
}
break;
case EKeyType::MouseInput:
case EKeyType::Joystick:
{
// Generate paired X/Y axis keys for 2D input
const FKey KeyX{GetXAxisName(ActionName)};
const FKey KeyY{GetYAxisName(ActionName)};
const FKey Key{ActionName};
EKeys::AddKey({KeyX, KeyX.GetDisplayName(),
FKeyDetails::GamepadKey | FKeyDetails::Axis1D, MenuCategory});
EKeys::AddKey({KeyY, KeyY.GetDisplayName(),
FKeyDetails::GamepadKey | FKeyDetails::Axis1D, MenuCategory});
EKeys::AddPairedKey({Key, Key.GetDisplayName(),
FKeyDetails::GamepadKey | FKeyDetails::Axis2D, MenuCategory},
KeyX, KeyY);
}
break;
}
}
// In USteamInputSettings class - actual configuration structure
UPROPERTY(Config, EditAnywhere, Category = "Actions")
TMap<FName, EKeyType> Keys;
// Example configuration in DefaultInput.ini:
[/Script/SteamToolsInput.SteamInputSettings]
Keys=(("Move", EKeyType::Joystick))
Keys=(("Look", EKeyType::MouseInput))
Keys=(("Jump", EKeyType::Button))
Keys=(("Interact", EKeyType::Button))
Keys=(("Fire", EKeyType::Button))
Keys=(("AimTrigger", EKeyType::Analog))
// Slate navigation bindings
KeyEventRules=(("Move_AxisY", EUINavigation::Up))
KeyEventRules=(("Move_AxisY", EUINavigation::Down))
KeyEventRules=(("Move_AxisX", EUINavigation::Left))
KeyEventRules=(("Move_AxisX", EUINavigation::Right))
KeyActionRules=(("Interact", EUINavigationAction::Accept))
KeyActionRules=(("MenuBack", EUINavigationAction::Back))
Runtime-configurable UI navigation that integrates directly with Unreal's Slate system:
void USteamInputSettings::Internal_ApplySlateConfig() const
{
if (FSlateApplication::IsInitialized())
{
const TSharedRef<FNavigationConfig> Config =
FSlateApplication::Get().GetNavigationConfig();
// Apply user-configured key bindings for UI navigation
Config->KeyActionRules = KeyActionRules; // Accept/Back actions
Config->KeyEventRules = KeyEventRules; // Directional navigation
}
}
// Settings expose these for runtime customization:
UPROPERTY(Config, EditAnywhere, Category = "Slate Input")
TMap<FKey, EUINavigation> KeyEventRules; // Up, Down, Left, Right
UPROPERTY(Config, EditAnywhere, Category = "Slate Input")
TMap<FKey, EUINavigationAction> KeyActionRules; // Accept, Back
Complete Blueprint integration for runtime Steam Input management:
// USteamInput class - Blueprint-accessible functions
UCLASS()
class STEAMTOOLSINPUT_API USteamInput : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
static void ActivateActionSet(int32 ControllerID, FInputActionSetHandle ActionSet);
UFUNCTION(BlueprintCallable, BlueprintPure)
static FInputHandle GetControllerHandle(uint8 PlayerIndex);
UFUNCTION(BlueprintCallable, BlueprintPure)
static FInputActionSetHandle GetActionSetHandle(FName ActionSetName);
UFUNCTION(BlueprintCallable)
static bool ShowBindingPanel(int32 ControllerID);
UFUNCTION(BlueprintCallable, BlueprintPure)
static FControllerActionHandle GetActionHandle(FName ActionName);
};
The plugin is organized into focused modules for maintainability and extensibility:
{
"Modules": [
{
"Name": "SteamToolsCore",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "SteamToolsInput",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "SteamAchievements",
"Type": "Runtime",
"LoadingPhase": "Default"
}
],
"Plugins": [
{
"Name": "SteamShared",
"Enabled": true
},
{
"Name": "EnhancedInput",
"Enabled": true
}
]
}
The plugin is open source and production-ready. Drop it into your project and start using Steam Input in minutes.