ButtonRepeatDelay
ButtonRepeatDelay
#Overview
name: ButtonRepeatDelay
The value of this variable can be defined or overridden in .ini config files. 1
.ini config file referencing this setting variable.
It is referenced in 15
C++ source files.
#Summary
#Usage in the C++ source code
The purpose of ButtonRepeatDelay is to control the delay between repeated button press events when a button is held down on input devices such as controllers or gamepads.
This setting variable is primarily used in input handling systems across various platforms and input devices in Unreal Engine 5. Based on the callsites, it’s utilized in the following subsystems and plugins:
- OpenXR Input Plugin
- Steam Controller Plugin
- XInput Device Plugin (for Windows)
- Android Input Interface
- HID Input Interface (for Mac)
The value of this variable is typically set in the engine configuration files, specifically in the GInputIni file under the “/Script/Engine.InputSettings” section. It’s often read alongside another variable called InitialButtonRepeatDelay, which sets the initial delay before the first repeat event.
ButtonRepeatDelay interacts with other variables such as:
- InitialButtonRepeatDelay
- NextRepeatTime (for individual buttons)
- ControllerState structures
Developers should be aware of the following when using this variable:
- It affects the responsiveness of held button inputs across various input devices.
- The value is in seconds for some implementations and in nanoseconds for others (e.g., OpenXR uses nanoseconds).
- It’s platform-specific and may behave differently across different input systems.
Best practices when using this variable include:
- Ensure the value is appropriate for your game’s input requirements. A shorter delay increases responsiveness but may lead to unintended rapid input.
- Consider exposing this setting to players, allowing them to adjust it to their preferences.
- Test thoroughly across all supported input devices and platforms to ensure consistent behavior.
- Be mindful of the units (seconds vs. nanoseconds) when setting or reading this value in different subsystems.
- Consider the relationship between InitialButtonRepeatDelay and ButtonRepeatDelay for a smooth input experience.
#Setting Variables
#References In INI files
Location: <Workspace>/Engine/Config/BaseInput.ini:20, section: [/Script/Engine.InputSettings]
- INI Section:
/Script/Engine.InputSettings
- Raw value:
0.1
- Is Array:
False
#References in C++ code
#Callsites
This variable is referenced in the following C++ source code:
#Loc: <Workspace>/Engine/Plugins/Runtime/OpenXR/Source/OpenXRInput/Private/OpenXRInput.cpp:971
Scope (from outer to inner):
file
function void FOpenXRInputPlugin::FOpenXRInput::SendControllerEvents
Source code excerpt:
// TODO: We should retrieve the current time rather than the display time
MessageHandler->OnControllerButtonPressed(Action.Name, DeviceMapper.GetPrimaryPlatformUser(), DeviceMapper.GetDefaultInputDevice(), /*IsRepeat =*/true);
Action.NextRepeatTime = OpenXRHMD->GetDisplayTime() + ButtonRepeatDelay;
}
}
break;
case XR_ACTION_TYPE_FLOAT_INPUT:
{
XrActionStateFloat State;
#Loc: <Workspace>/Engine/Plugins/Runtime/OpenXR/Source/OpenXRInput/Private/OpenXRInput.h:177
Scope (from outer to inner):
file
class class FOpenXRInputPlugin : public IOpenXRInputPlugin
class class FOpenXRInput : public IOpenXRInputModule, public IInputDevice, public FXRMotionControllerBase, public IHapticDevice, public TSharedFromThis<FOpenXRInput>
Source code excerpt:
/** Repeat key delays */
const XrTime InitialButtonRepeatDelay = 2e8;
const XrTime ButtonRepeatDelay = 1e8;
bool BuildActions(XrSession Session);
void SyncActions(XrSession Session);
void BuildLegacyActions(TMap<FString, FInteractionProfile>& Profiles);
void BuildEnhancedActions(TMap<FString, FInteractionProfile>& Profiles);
void DestroyActions();
#Loc: <Workspace>/Engine/Plugins/Runtime/Steam/SteamController/Source/SteamController/Private/SteamController.cpp:27
Scope (from outer to inner):
file
class class FSteamController : public IInputDevice
function FSteamController
Source code excerpt:
FSteamController(const TSharedRef< FGenericApplicationMessageHandler >& InMessageHandler) :
InitialButtonRepeatDelay(0.2),
ButtonRepeatDelay(0.1),
MessageHandler(InMessageHandler),
bSteamControllerInitialized(false),
InputSettings(nullptr)
{
GConfig->GetDouble(TEXT("/Script/Engine.InputSettings"), TEXT("InitialButtonRepeatDelay"), InitialButtonRepeatDelay, GInputIni);
GConfig->GetDouble(TEXT("/Script/Engine.InputSettings"), TEXT("ButtonRepeatDelay"), ButtonRepeatDelay, GInputIni);
// Initialize the API, so we can start calling SteamController functions
SteamAPIHandle = FSteamSharedModule::Get().ObtainSteamClientInstanceHandle();
// [RCL] 2015-01-23 FIXME: move to some other code than constructor so we can handle failures more gracefully
if (SteamAPIHandle.IsValid() && (SteamInput() != nullptr))
#Loc: <Workspace>/Engine/Plugins/Runtime/Steam/SteamController/Source/SteamController/Private/SteamController.cpp:153
Scope (from outer to inner):
file
class class FSteamController : public IInputDevice
function virtual void SendControllerEvents
Source code excerpt:
{
MessageHandler->OnControllerButtonPressed(DigitalNamesToKeysMap[DigitalActionName].GetFName(), UserId, DeviceId, false);
ControllerState.DigitalRepeatTimeMap[DigitalActionName] = FPlatformTime::Seconds() + ButtonRepeatDelay;
}
else if (ControllerState.DigitalStatusMap[DigitalActionName] == true && !DigitalActionData.bState)
{
MessageHandler->OnControllerButtonReleased(DigitalNamesToKeysMap[DigitalActionName].GetFName(), UserId, DeviceId, false);
}
else if (ControllerState.DigitalStatusMap[DigitalActionName] == true && DigitalActionData.bState && ControllerState.DigitalRepeatTimeMap[DigitalActionName] <= CurrentTime)
{
ControllerState.DigitalRepeatTimeMap[DigitalActionName] += ButtonRepeatDelay;
MessageHandler->OnControllerButtonPressed(DigitalNamesToKeysMap[DigitalActionName].GetFName(), UserId, DeviceId, true);
}
ControllerState.DigitalStatusMap[DigitalActionName] = DigitalActionData.bState;
}
#Loc: <Workspace>/Engine/Plugins/Runtime/Steam/SteamController/Source/SteamController/Private/SteamController.cpp:325
Scope (from outer to inner):
file
class class FSteamController : public IInputDevice
Source code excerpt:
/** Delay before sending a repeat message after a button has been pressed for a while */
double ButtonRepeatDelay;
/** handler to send all messages to */
TSharedRef<FGenericApplicationMessageHandler> MessageHandler;
/** SteamAPI initialized **/
TSharedPtr<class FSteamClientInstanceHandler> SteamAPIHandle;
#Loc: <Workspace>/Engine/Plugins/Runtime/Windows/XInputDevice/Source/XInputDevice/Private/XInputInterface.cpp:43
Scope (from outer to inner):
file
function XInputInterface::XInputInterface
Source code excerpt:
bNeedsControllerStateUpdate = true;
InitialButtonRepeatDelay = 0.2f;
ButtonRepeatDelay = 0.1f;
GConfig->GetFloat(TEXT("/Script/Engine.InputSettings"), TEXT("InitialButtonRepeatDelay"), InitialButtonRepeatDelay, GInputIni);
GConfig->GetFloat(TEXT("/Script/Engine.InputSettings"), TEXT("ButtonRepeatDelay"), ButtonRepeatDelay, GInputIni);
// In the engine, all controllers map to xbox controllers for consistency
X360ToXboxControllerMapping[0] = 0; // A
X360ToXboxControllerMapping[1] = 1; // B
X360ToXboxControllerMapping[2] = 2; // X
X360ToXboxControllerMapping[3] = 3; // Y
#Loc: <Workspace>/Engine/Plugins/Runtime/Windows/XInputDevice/Source/XInputDevice/Private/XInputInterface.cpp:266
Scope (from outer to inner):
file
function void XInputInterface::SendControllerEvents
Source code excerpt:
MessageHandler->OnControllerButtonPressed( Buttons[ButtonIndex], PlatformUser, InputDevice, true );
// set the button's NextRepeatTime to the ButtonRepeatDelay
ControllerState.NextRepeatTime[ButtonIndex] = CurrentTime + ButtonRepeatDelay;
}
// Update the state for next time
ControllerState.ButtonStates[ButtonIndex] = CurrentStates[ButtonIndex];
}
#Loc: <Workspace>/Engine/Plugins/Runtime/Windows/XInputDevice/Source/XInputDevice/Private/XInputInterface.h:134
Scope (from outer to inner):
file
class class XInputInterface : public IInputDevice
Source code excerpt:
/** Delay before sending a repeat message after a button has been pressed for a while */
float ButtonRepeatDelay;
/** */
FGamepadKeyNames::Type Buttons[MAX_NUM_CONTROLLER_BUTTONS];
/** */
TSharedRef<FGenericApplicationMessageHandler> MessageHandler;
#Loc: <Workspace>/Engine/Source/Runtime/ApplicationCore/Private/Android/AndroidInputInterface.cpp:38
Scope: file
Source code excerpt:
FGamepadKeyNames::Type FAndroidInputInterface::ButtonMapping[MAX_NUM_CONTROLLER_BUTTONS];
float FAndroidInputInterface::InitialButtonRepeatDelay;
float FAndroidInputInterface::ButtonRepeatDelay;
FDeferredAndroidMessage FAndroidInputInterface::DeferredMessages[MAX_DEFERRED_MESSAGE_QUEUE_SIZE];
int32 FAndroidInputInterface::DeferredMessageQueueLastEntryIndex = 0;
int32 FAndroidInputInterface::DeferredMessageQueueDroppedCount = 0;
TArray<FAndroidInputInterface::MotionData> FAndroidInputInterface::MotionDataStack
#Loc: <Workspace>/Engine/Source/Runtime/ApplicationCore/Private/Android/AndroidInputInterface.cpp:131
Scope (from outer to inner):
file
function FAndroidInputInterface::FAndroidInputInterface
Source code excerpt:
InitialButtonRepeatDelay = 0.2f;
ButtonRepeatDelay = 0.1f;
GConfig->GetFloat(TEXT("/Script/Engine.InputSettings"), TEXT("InitialButtonRepeatDelay"), InitialButtonRepeatDelay, GInputIni);
GConfig->GetFloat(TEXT("/Script/Engine.InputSettings"), TEXT("ButtonRepeatDelay"), ButtonRepeatDelay, GInputIni);
CurrentVibeIntensity = 0;
FMemory::Memset(VibeValues, 0);
FMemory::Memset(DeviceMapping, 0);
FMemory::Memset(OldControllerData, 0);
#Loc: <Workspace>/Engine/Source/Runtime/ApplicationCore/Private/Android/AndroidInputInterface.cpp:1337
Scope (from outer to inner):
file
function void FAndroidInputInterface::SendControllerEvents
Source code excerpt:
MessageHandler->OnControllerButtonPressed(ButtonMapping[ButtonIndex], UserId, DeviceId, true);
// Set the button's NextRepeatTime to the ButtonRepeatDelay
NewControllerState.NextRepeatTime[ButtonIndex] = CurrentTime + ButtonRepeatDelay;
}
}
// send controller force feedback updates if enabled
if (GAndroidUseControllerFeedback != 0)
{
#Loc: <Workspace>/Engine/Source/Runtime/ApplicationCore/Private/Mac/HIDInputInterface.cpp:37
Scope (from outer to inner):
file
function HIDInputInterface::HIDInputInterface
Source code excerpt:
bIsGamepadAttached = false;
InitialButtonRepeatDelay = 0.2f;
ButtonRepeatDelay = 0.1f;
GConfig->GetFloat(TEXT("/Script/Engine.InputSettings"), TEXT("InitialButtonRepeatDelay"), InitialButtonRepeatDelay, GInputIni);
GConfig->GetFloat(TEXT("/Script/Engine.InputSettings"), TEXT("ButtonRepeatDelay"), ButtonRepeatDelay, GInputIni);
Buttons[0] = FGamepadKeyNames::FaceButtonBottom;
Buttons[1] = FGamepadKeyNames::FaceButtonRight;
Buttons[2] = FGamepadKeyNames::FaceButtonLeft;
Buttons[3] = FGamepadKeyNames::FaceButtonTop;
Buttons[4] = FGamepadKeyNames::LeftShoulder;
#Loc: <Workspace>/Engine/Source/Runtime/ApplicationCore/Private/Mac/HIDInputInterface.cpp:584
Scope (from outer to inner):
file
function void HIDInputInterface::SendControllerEvents
Source code excerpt:
MessageHandler->OnControllerButtonPressed(Buttons[ButtonIndex], UserId, DeviceId, true);
// set the button's NextRepeatTime to the ButtonRepeatDelay
ControllerState.NextRepeatTime[ButtonIndex] = CurrentTime + ButtonRepeatDelay;
}
// Update the state for next time
ControllerState.ButtonStates[ButtonIndex] = CurrentButtonStates[ButtonIndex];
}
}
#Loc: <Workspace>/Engine/Source/Runtime/ApplicationCore/Private/Mac/HIDInputInterface.h:116
Scope (from outer to inner):
file
class class HIDInputInterface
Source code excerpt:
/** Delay before sendign a repeat message after a button has been pressed for a while */
float ButtonRepeatDelay;
bool bIsGamepadAttached;
IOHIDManagerRef HIDManager;
TSharedRef<FGenericApplicationMessageHandler> MessageHandler;
#Loc: <Workspace>/Engine/Source/Runtime/ApplicationCore/Public/Android/AndroidInputInterface.h:362
Scope (from outer to inner):
file
class class FAndroidInputInterface : public IInputInterface
Source code excerpt:
static float InitialButtonRepeatDelay;
static float ButtonRepeatDelay;
static FDeferredAndroidMessage DeferredMessages[MAX_DEFERRED_MESSAGE_QUEUE_SIZE];
static int32 DeferredMessageQueueLastEntryIndex;
static int32 DeferredMessageQueueDroppedCount;
static TArray<MotionData> MotionDataStack;