The OmniShift Input System provides a comprehensive, modern input framework built on Unreal Engine 5's Enhanced Input system. It offers flexible input profiling, platform-specific optimizations, VR/AR support, accessibility features, and runtime reconfiguration capabilities designed for professional game development.
The system supports multiple input devices simultaneously, provides seamless cross-platform compatibility, and includes advanced features like input context management, sensitivity profiles, and comprehensive debugging tools.
The input system is built around a data-driven architecture with input profiles as the central configuration mechanism:
Input System Architecture
├── Enhanced Input Foundation
│ ├── UInputAction (Action Definitions)
│ ├── UInputMappingContext (Context Management)
│ └── FEnhancedActionKeyMapping (Key Bindings)
├── Input Profile System
│ ├── UGASPAC_InputProfileDataAsset (Configuration)
│ ├── FGASPAC_InputMapping (Action-Key Mapping)
│ ├── FGASPAC_InputContextSettings (Context Configuration)
│ └── Platform-Specific Settings
├── Input Type Support
│ ├── Mouse & Keyboard
│ ├── Gamepad Controllers
│ ├── VR Controllers
│ ├── AR Touch/Gesture
│ └── Accessibility Devices
└── Runtime Management
├── Input Detection
├── Context Switching
├── Sensitivity Adjustment
└── Debug Visualization
The core of the input system is the UGASPAC_InputProfileDataAsset, which centralizes all input configuration:
class OMNISHIFT_API UGASPAC_InputProfileDataAsset : public UDataAsset
{
// Profile Information
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Profile")
FName ProfileName = TEXT("DefaultInputProfile");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Profile")
FText ProfileDescription;
// Input Contexts and Mappings
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Configuration")
TArray<FGASPAC_InputContextSettings> InputContexts;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Configuration")
TArray<FGASPAC_InputMapping> InputMappings;
// Default Input Actions
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default Actions")
UInputAction* IA_Move = nullptr;
UInputAction* IA_Look = nullptr;
UInputAction* IA_Jump = nullptr;
// ... additional default actions
};
Each input mapping defines how an action is bound to input devices:
USTRUCT(BlueprintType)
struct FGASPAC_InputMapping
{
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Mapping")
TObjectPtr<UInputAction> InputAction = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Mapping")
TArray<FEnhancedActionKeyMapping> KeyMappings;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Mapping")
bool bEnabled = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Mapping")
int32 Priority = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Mapping")
FName MappingName = NAME_None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Mapping")
FText MappingDescription;
};
Context management allows for dynamic input switching based on game state:
USTRUCT(BlueprintType)
struct FGASPAC_InputContextSettings
{
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Context Settings")
TObjectPtr<UInputMappingContext> MappingContext = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Context Settings")
int32 Priority = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Context Settings")
bool bRegisterWhenInitialized = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Context Settings")
bool bIgnoreAllPressedKeysUntilRelease = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Context Settings")
bool bAutoInhibit = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Context Settings")
TArray<FName> ExcludedActionNames;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Context Settings")
TArray<FName> RequiredActionNames;
};
The system provides a comprehensive set of predefined input actions for common game functionality:
| Action Name | Value Type | Description | Common Bindings |
|---|---|---|---|
| IA_Move | Vector2D | Character movement | WASD, Left Stick |
| IA_Look | Vector2D | Camera/control look | Mouse, Right Stick |
| IA_Jump | Boolean | Jump initiation | Space, A, Cross |
| IA_Crouch | Boolean | Crouch toggle/hold | Left Ctrl, B, Circle |
| IA_Sprint | Boolean | Sprint toggle/hold | Left Shift, R3 |
| Action Name | Value Type | Description | Common Bindings |
|---|---|---|---|
| IA_SwitchCamera | Boolean | Cycle camera modes | V, D-Pad Right |
| IA_FreeLook | Boolean | Free look mode | Left Alt, L3 |
| IA_ZoomIn | Boolean | Camera zoom in | Mouse Wheel Up, R1 |
| IA_ZoomOut | Boolean | Camera zoom out | Mouse Wheel Down, L1 |
| Action Name | Value Type | Description | Common Bindings |
|---|---|---|---|
| IA_InteractionHand | Boolean | Hand interaction | E, Square |
| IA_PrimaryAction | Boolean | Primary ability/action | Left Mouse, R2 |
| IA_SecondaryAction | Boolean | Secondary ability/action | Right Mouse, L2 |
| IA_TertiaryAction | Boolean | Tertiary ability/action | Middle Mouse, R3 |
| IA_EnterRagdoll | Boolean | Debug ragdoll mode | R, D-Pad Down |
USTRUCT(BlueprintType)
struct FGASPAC_InputSensitivitySettings
{
// Sensitivity Multipliers
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sensitivity")
float MouseSensitivity = 1.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sensitivity")
float MouseAcceleration = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sensitivity")
float MouseSmoothing = 0.0f;
// Inversion Settings
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sensitivity")
bool bInvertMouseY = false;
};
// Gamepad-specific settings
float GamepadSensitivity = 1.0f;
float GamepadAcceleration = 0.0f;
float GamepadSmoothing = 0.0f;
bool bInvertGamepadY = false;
bool bEnableGamepadInput = true;
Comprehensive VR input support with haptic feedback:
USTRUCT(BlueprintType)
struct FGASPAC_VRInputSettings
{
// VR Controller Actions
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Input")
TObjectPtr<UInputAction> VR_GripLeft = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Input")
TObjectPtr<UInputAction> VR_GripRight = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Input")
TObjectPtr<UInputAction> VR_TriggerLeft = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Input")
TObjectPtr<UInputAction> VR_TriggerRight = nullptr;
// VR Analog Controls
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Input")
TObjectPtr<UInputAction> VR_ThumbstickLeft = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Input")
TObjectPtr<UInputAction> VR_ThumbstickRight = nullptr;
// Dead Zone Settings
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Input")
float VRThumbstickDeadZone = 0.2f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Input")
float VRTrackpadDeadZone = 0.2f;
// Haptic Feedback
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Input")
bool bEnableVRHapticFeedback = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Input")
float VRHapticIntensity = 1.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Input")
float VRHapticDuration = 0.1f;
};
The system includes comprehensive accessibility options:
USTRUCT(BlueprintType)
struct FGASPAC_AccessibilitySettings
{
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Accessibility")
bool bEnableAccessibility = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Accessibility")
bool bSimplifyControls = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Accessibility")
bool bEnableHoldToToggle = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Accessibility")
float HoldToToggleTime = 1.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Accessibility")
bool bEnableVisualIndicators = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Accessibility")
bool bEnableAudioIndicators = true;
};
// Create and configure input profile in C++
UGASPAC_InputProfileDataAsset* CreateDefaultInputProfile()
{
UGASPAC_InputProfileDataAsset* InputProfile = NewObject<UGASPAC_InputProfileDataAsset>();
InputProfile->ProfileName = TEXT("DefaultGameProfile");
InputProfile->ProfileDescription = FText::FromString("Default game input profile");
// Configure platform settings
InputProfile->bEnableMouseInput = true;
InputProfile->bEnableGamepadInput = true;
InputProfile->bEnableTouchInput = false;
InputProfile->bEnableVRInput = false;
InputProfile->bAutoDetectInputType = true;
// Configure sensitivity
InputProfile->SensitivitySettings.MouseSensitivity = 1.0f;
InputProfile->SensitivitySettings.GamepadSensitivity = 1.2f;
InputProfile->SensitivitySettings.bInvertMouseY = false;
return InputProfile;
}
// Character class with input profile integration
class AMyGameCharacter : public AGASPAC_CharacterBase
{
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UGASPAC_InputProfileDataAsset* InputProfile = nullptr;
virtual void BeginPlay() override
{
Super::BeginPlay();
// Apply input profile
if (InputProfile)
{
ApplyInputProfile(InputProfile);
}
}
void ApplyInputProfile(UGASPAC_InputProfileDataAsset* Profile)
{
if (!Profile || !GetLocalPlayer()) return;
UEnhancedInputLocalPlayerSubsystem* Subsystem = GetLocalPlayer()->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
if (!Subsystem) return;
// Clear existing contexts
Subsystem->ClearAllMappingContexts();
// Add input contexts from profile
for (const auto& ContextSettings : Profile->InputContexts)
{
if (ContextSettings.MappingContext)
{
Subsystem->AddMappingContext(ContextSettings.MappingContext, ContextSettings.Priority);
}
}
// Set default actions
SetupDefaultInputActions(Profile);
}
void SetupDefaultInputActions(UGASPAC_InputProfileDataAsset* Profile)
{
// Bind default actions to character functions
if (Profile->IA_Move)
{
MoveAction = Profile->IA_Move;
}
if (Profile->IA_Look)
{
LookAction = Profile->IA_Look;
}
if (Profile->IA_Jump)
{
JumpAction = Profile->IA_Jump;
}
if (Profile->IA_Sprint)
{
SprintAction = Profile->IA_Sprint;
}
if (Profile->IA_InteractionHand)
{
InteractAction = Profile->IA_InteractionHand;
}
}
};
// Input context manager for different game states
class UMyInputContextManager : public UActorComponent
{
public:
UFUNCTION(BlueprintCallable, Category = "Input Context")
void SwitchToGameplayContext()
{
if (GameplayProfile)
{
ApplyInputContext(GameplayProfile, 0);
}
}
UFUNCTION(BlueprintCallable, Category = "Input Context")
void SwitchToUIContext()
{
if (UIProfile)
{
ApplyInputContext(UIProfile, 10); // Higher priority for UI
}
}
UFUNCTION(BlueprintCallable, Category = "Input Context")
void SwitchToVehicleContext()
{
if (VehicleProfile)
{
ApplyInputContext(VehicleProfile, 5);
}
}
private:
void ApplyInputContext(UGASPAC_InputProfileDataAsset* Profile, int32 Priority)
{
ULocalPlayer* LocalPlayer = GetLocalPlayer();
if (!LocalPlayer) return;
UEnhancedInputLocalPlayerSubsystem* Subsystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
if (!Subsystem) return;
// Clear existing contexts
Subsystem->ClearAllMappingContexts();
// Apply new contexts
for (const auto& ContextSettings : Profile->InputContexts)
{
if (ContextSettings.MappingContext)
{
Subsystem->AddMappingContext(ContextSettings.MappingContext, Priority + ContextSettings.Priority);
}
}
}
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Profiles")
UGASPAC_InputProfileDataAsset* GameplayProfile = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Profiles")
UGASPAC_InputProfileDataAsset* UIProfile = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Profiles")
UGASPAC_InputProfileDataAsset* VehicleProfile = nullptr;
};
// Runtime input remapping system
class UMyInputRemapper : public UActorComponent
{
public:
UFUNCTION(BlueprintCallable, Category = "Input Remapping")
void RemapAction(UInputAction* Action, const FKey& NewKey)
{
if (!CurrentInputProfile) return;
// Remove existing mapping for this key
RemoveKeyMapping(NewKey);
// Add new mapping
FGASPAC_InputMapping NewMapping;
NewMapping.InputAction = Action;
NewMapping.MappingName = FName(*FString::Printf(TEXT("Runtime_%s_%s"),
*Action->GetName(), *NewKey.ToString()));
FEnhancedActionKeyMapping KeyMapping;
KeyMapping.Action = Action;
KeyMapping.Key = NewKey;
KeyMapping.bInvertResult = false;
KeyMapping.bSwappedInputAxis = false;
NewMapping.KeyMappings.Add(KeyMapping);
NewMapping.bEnabled = true;
CurrentInputProfile->AddInputMapping(NewMapping);
// Apply changes
ApplyInputProfileChanges();
}
UFUNCTION(BlueprintCallable, Category = "Input Remapping")
void SaveInputProfile()
{
if (!CurrentInputProfile) return;
// Save profile to config file
FString SavePath = FPaths::ProjectSavedDir() / TEXT("InputProfiles") /
FString::Printf(TEXT("%s.json"), *CurrentInputProfile->ProfileName.ToString());
// Serialize profile to JSON (implementation depends on your serialization approach)
SaveProfileToFile(CurrentInputProfile, SavePath);
}
UFUNCTION(BlueprintCallable, Category = "Input Remapping")
void LoadInputProfile(FName ProfileName)
{
FString LoadPath = FPaths::ProjectSavedDir() / TEXT("InputProfiles") /
FString::Printf(TEXT("%s.json"), *ProfileName.ToString());
// Load profile from file
UGASPAC_InputProfileDataAsset* LoadedProfile = LoadProfileFromFile(LoadPath);
if (LoadedProfile)
{
CurrentInputProfile = LoadedProfile;
ApplyInputProfileChanges();
}
}
private:
void ApplyInputProfileChanges()
{
if (!CurrentInputProfile) return;
// Re-apply input profile with current mappings
// This would typically call the character's ApplyInputProfile method
OnInputProfileChanged.Broadcast(CurrentInputProfile);
}
void RemoveKeyMapping(const FKey& Key)
{
if (!CurrentInputProfile) return;
// Find and remove mappings for the specified key
TArray<FGASPAC_InputMapping>& Mappings = CurrentInputProfile->InputMappings;
for (int32 i = Mappings.Num() - 1; i >= 0; --i)
{
for (int32 j = Mappings[i].KeyMappings.Num() - 1; j >= 0; --j)
{
if (Mappings[i].KeyMappings[j].Key == Key)
{
Mappings[i].KeyMappings.RemoveAt(j);
}
}
}
}
UPROPERTY(BlueprintReadOnly, Category = "Input Remapping")
UGASPAC_InputProfileDataAsset* CurrentInputProfile = nullptr;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnInputProfileChanged, UGASPAC_InputProfileDataAsset*, NewProfile);
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnInputProfileChanged OnInputProfileChanged;
};
// VR-specific input handling
class AMyVRCharacter : public AGASPAC_CharacterBase
{
public:
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (InputProfile && InputProfile->HasVRInput())
{
SetupVRInput();
}
}
protected:
void SetupVRInput()
{
const FGASPAC_VRInputSettings& VRSettings = InputProfile->VRInputSettings;
// VR movement
if (VRSettings.VR_ThumbstickLeft)
{
InputComponent->BindAction(VRSettings.VR_ThumbstickLeft, ETriggerEvent::Triggered,
this, &AMyVRCharacter::HandleVRMove);
}
// VR rotation
if (VRSettings.VR_ThumbstickRight)
{
InputComponent->BindAction(VRSettings.VR_ThumbstickRight, ETriggerEvent::Triggered,
this, &AMyVRCharacter::HandleVRRotate);
}
// VR interactions
if (VRSettings.VR_GripLeft)
{
InputComponent->BindAction(VRSettings.VR_GripLeft, ETriggerEvent::Started,
this, &AMyVRCharacter::HandleVRGripLeft);
InputComponent->BindAction(VRSettings.VR_GripLeft, ETriggerEvent::Completed,
this, &AMyVRCharacter::HandleVRReleaseLeft);
}
if (VRSettings.VR_GripRight)
{
InputComponent->BindAction(VRSettings.VR_GripRight, ETriggerEvent::Started,
this, &AMyVRCharacter::HandleVRGripRight);
InputComponent->BindAction(VRSettings.VR_GripRight, ETriggerEvent::Completed,
this, &AMyVRCharacter::HandleVRReleaseRight);
}
// VR triggers
if (VRSettings.VR_TriggerLeft)
{
InputComponent->BindAction(VRSettings.VR_TriggerLeft, ETriggerEvent::Started,
this, &AMyVRCharacter::HandleVRTriggerLeft);
}
if (VRSettings.VR_TriggerRight)
{
InputComponent->BindAction(VRSettings.VR_TriggerRight, ETriggerEvent::Started,
this, &AMyVRCharacter::HandleVRTriggerRight);
}
}
void HandleVRMove(const FInputActionValue& Value)
{
const FVector2D MovementVector = Value.Get<FVector2D>();
// Apply dead zone
const float DeadZone = InputProfile->VRInputSettings.VRThumbstickDeadZone;
FVector2D FilteredMovement = MovementVector;
if (MovementVector.Size() < DeadZone)
{
FilteredMovement = FVector2D::ZeroVector;
}
else
{
FilteredMovement = MovementVector.GetSafeNormal() *
((MovementVector.Size() - DeadZone) / (1.0f - DeadZone));
}
// Apply movement to character
AddMovementInput(GetActorForwardVector(), FilteredMovement.Y);
AddMovementInput(GetActorRightVector(), FilteredMovement.X);
}
void HandleVRGripLeft()
{
if (InteractionHandComponent)
{
// Start left hand interaction
InteractionHandComponent->StartGrab(EControllerHand::Left);
}
// Trigger haptic feedback
if (InputProfile->VRInputSettings.bEnableVRHapticFeedback)
{
TriggerVRHapticFeedback(EControllerHand::Left,
InputProfile->VRInputSettings.VRHapticIntensity);
}
}
void TriggerVRHapticFeedback(EControllerHand Hand, float Intensity)
{
APlayerController* PC = Cast<APlayerController>(GetController());
if (!PC) return;
// Get VR pawn/controller interface
IVRInterfaceInterface* VRInterface = Cast<IVRInterfaceInterface>(PC);
if (VRInterface)
{
VRInterface->PlayHapticFeedback(Hand, Intensity,
InputProfile->VRInputSettings.VRHapticDuration);
}
}
};
// Input debugging system
class UMyInputDebugger : public UActorComponent
{
public:
virtual void BeginPlay() override
{
Super::BeginPlay();
// Initialize debug visualization
if (GEngine && bShowDebugInput)
{
SetupDebugHUD();
}
}
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (bShowDebugInput)
{
UpdateDebugInfo(DeltaTime);
}
}
UFUNCTION(BlueprintCallable, Category = "Input Debug")
void EnableInputDebug(bool bEnable = true)
{
bShowDebugInput = bEnable;
if (bEnable)
{
UE_LOG(LogInput, Log, TEXT("Input debugging enabled"));
SetupDebugHUD();
}
else
{
UE_LOG(LogInput, Log, TEXT("Input debugging disabled"));
}
}
private:
void UpdateDebugInfo(float DeltaTime)
{
if (!InputProfile) return;
// Update debug display with current input state
FString DebugInfo = FString::Printf(TEXT(
"=== Input Debug Info ===\n"
"Profile: %s\n"
"Mouse Sens: %.2f\n"
"Gamepad Sens: %.2f\n"
"Active Contexts: %d\n"
"Active Mappings: %d"
),
*InputProfile->ProfileName.ToString(),
InputProfile->SensitivitySettings.MouseSensitivity,
InputProfile->SensitivitySettings.GamepadSensitivity,
GetActiveContextCount(),
GetActiveMappingCount()
);
// Display on screen
if (GEngine && GEngine->bEnableOnScreenDebugMessages)
{
GEngine->AddOnScreenDebugMessage(-1, 0.1f, FColor::Yellow, DebugInfo);
}
// Log input events if enabled
if (InputProfile->bLogInputEvents)
{
LogInputEvents(DeltaTime);
}
}
int32 GetActiveContextCount() const
{
if (!GetLocalPlayer()) return 0;
UEnhancedInputLocalPlayerSubsystem* Subsystem = GetLocalPlayer()->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
return Subsystem ? Subsystem->GetMappingContexts().Num() : 0;
}
int32 GetActiveMappingCount() const
{
if (!InputProfile) return 0;
int32 Count = 0;
for (const auto& Mapping : InputProfile->InputMappings)
{
if (Mapping.bEnabled)
{
Count += Mapping.KeyMappings.Num();
}
}
return Count;
}
void LogInputEvents(float DeltaTime)
{
// Implementation would log actual input events
// This is a placeholder for the concept
AccumulatedTime += DeltaTime;
if (AccumulatedTime >= 1.0f)
{
UE_LOG(LogInput, Verbose, TEXT("Input events logged in the last second"));
AccumulatedTime = 0.0f;
}
}
void SetupDebugHUD()
{
// Setup custom HUD for input visualization
// Implementation depends on your HUD system
}
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Debug")
bool bShowDebugInput = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Debug")
bool bLogInputEvents = false;
UPROPERTY()
float AccumulatedTime = 0.0f;
};
// Optimized input processing
class UMyOptimizedInputProcessor : public UActorComponent
{
public:
virtual void BeginPlay() override
{
Super::BeginPlay();
// Configure input update frequency
LastInputUpdateTime = 0.0f;
InputUpdateInterval = 1.0f / InputUpdateFrequency;
}
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// Throttle input processing for performance
LastInputUpdateTime += DeltaTime;
if (LastInputUpdateTime >= InputUpdateInterval)
{
ProcessInput(DeltaTime);
LastInputUpdateTime = 0.0f;
}
}
UFUNCTION(BlueprintCallable, Category = "Input Optimization")
void SetInputUpdateFrequency(float Frequency)
{
InputUpdateFrequency = FMath::Clamp(Frequency, 30.0f, 120.0f);
InputUpdateInterval = 1.0f / InputUpdateFrequency;
}
private:
void ProcessInput(float DeltaTime)
{
// Process input with optimizations
if (bEnableInputBatching)
{
ProcessBatchedInput(DeltaTime);
}
else
{
ProcessDirectInput(DeltaTime);
}
}
void ProcessBatchedInput(float DeltaTime)
{
// Batch multiple input events together
// This reduces the number of function calls
BatchedInputData.AccumulatedMovement += GetPendingMovement();
BatchedInputData.AccumulatedLook += GetPendingLook();
if (BatchedInputData.bShouldProcess)
{
ApplyBatchedInput();
BatchedInputData.Reset();
}
}
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance")
float InputUpdateFrequency = 60.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance")
bool bEnableInputBatching = true;
UPROPERTY()
float LastInputUpdateTime = 0.0f;
UPROPERTY()
float InputUpdateInterval = 0.01667f; // 60 Hz
struct FBatchedInputData
{
FVector AccumulatedMovement = FVector::ZeroVector;
FVector2D AccumulatedLook = FVector2D::ZeroVector;
bool bShouldProcess = false;
void Reset()
{
AccumulatedMovement = FVector::ZeroVector;
AccumulatedLook = FVector2D::ZeroVector;
bShouldProcess = false;
}
};
UPROPERTY()
FBatchedInputData BatchedInputData;
};
Problem: Input actions not responding
Solution:
Problem: Input lag or delayed response
Solution:
Problem: VR input not working
Solution:
Problem: Context switching not working
Solution:
For complete input system mastery:
Ready to implement advanced input systems? Check the Examples & Tutorials for complete working implementations of complex input scenarios.