InputHandler
The InputHandler is a specialized library component that manages Enhanced Input system integration for the Grid Placement System. It handles input mapping contexts and provides clean separation between placement and toggle input controls.
Overview
InputHandler acts as a focused wrapper around Unreal Engine's Enhanced Input system, managing the activation and deactivation of input mapping contexts. It provides a simple interface for enabling/disabling placement controls while keeping toggle functionality separate and always available.
Key Features
- ✅ Context Management - Handles Enhanced Input Mapping Context activation/deactivation
- ✅ Separation of Concerns - Keeps toggle and placement inputs separate
- ✅ Clean State Management - Ensures proper input state transitions
- ✅ Enhanced Input Integration - Full compatibility with UE5's Enhanced Input system
- ✅ Simple API - Minimal interface focused on essential functionality
Architecture
InputHandler manages two distinct input contexts:
-
Toggle Context - Always active when placement system is available
- Contains only the placement mode toggle action
- Remains active in both Normal and Placement modes
-
Placement Context - Active only during placement mode
- Contains all placement-specific actions (move, rotate, confirm, etc.)
- Higher priority than toggle context to override conflicting inputs
Core Methods
Toggle Input Management
EnableToggleInput
void EnableToggleInput(APlayerController* PC);
Enables the toggle input mapping context, allowing the player to enter placement mode.
Parameters:
PC
- Player controller to add the mapping context to
Usage:
// Called during system initialization
InputHandler->EnableToggleInput(PlayerController);
DisableToggleInput
void DisableToggleInput(APlayerController* PC);
Disables the toggle input mapping context.
Parameters:
PC
- Player controller to remove the mapping context from
Usage:
// Called during system shutdown
InputHandler->DisableToggleInput(PlayerController);
Placement Input Management
EnablePlacementInput
void EnablePlacementInput(APlayerController* PC);
Enables the placement input mapping context with higher priority than toggle context.
Parameters:
PC
- Player controller to add the mapping context to
Priority: 10 (higher than toggle context priority of 5)
Usage:
// Called when entering placement mode
InputHandler->EnablePlacementInput(PlayerController);
DisablePlacementInput
void DisablePlacementInput(APlayerController* PC);
Disables the placement input mapping context.
Parameters:
PC
- Player controller to remove the mapping context from
Usage:
// Called when exiting placement mode
InputHandler->DisablePlacementInput(PlayerController);
Configuration
Settings Methods
SetPlacementMappingContext
void SetPlacementMappingContext(UInputMappingContext* Context);
Sets the input mapping context used for placement mode.
Parameters:
Context
- The placement input mapping context
SetToggleMappingContext
void SetToggleMappingContext(UInputMappingContext* Context);
Sets the input mapping context used for toggle functionality.
Parameters:
Context
- The toggle input mapping context
Integration with PlacementSystemComponent
InputHandler is automatically configured and managed by PlacementSystemComponent:
Automatic Configuration
// In PlacementSystemComponent::ConfigureComponents()
if (InputHandler)
{
InputHandler->SetPlacementMappingContext(PlacementMappingContext);
InputHandler->SetToggleMappingContext(ToggleMappingContext);
}
Input State Management
// When entering placement mode
if (InputHandler)
{
APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
InputHandler->EnablePlacementInput(PC);
// Fire input mode change event
OnInputModeChanged.Broadcast(true, true); // placement active, toggle active
}
// When exiting placement mode
if (InputHandler)
{
APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
InputHandler->DisablePlacementInput(PC);
// Fire input mode change event
OnInputModeChanged.Broadcast(false, true); // placement inactive, toggle still active
}
System Lifecycle
// During initialization
if (InputHandler)
{
APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
InputHandler->EnableToggleInput(PC);
}
// During shutdown
if (InputHandler)
{
APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
InputHandler->DisablePlacementInput(PC);
InputHandler->DisableToggleInput(PC);
}
Input Mapping Context Structure
Toggle Mapping Context (IMC_PlacementToggle)
Priority: 5 Actions:
IA_PlacementToggle
- Toggle placement mode on/off
Placement Mapping Context (IMC_PlacementMode)
Priority: 10 (higher priority to override toggle when both are active) Actions:
IA_PlacementConfirm
- Confirm object placementIA_PlacementCancel
- Cancel placement or exit modeIA_PlacementRotate
- Rotate preview objectIA_PlacementZoom
- Zoom camera in/outIA_PlacementMove
- Move cameraIA_PlacementShift
- Hold for precision modeIA_SelectObject
- Select placed objects for editingIA_DeleteObject
- Delete selected objectsIA_PlacementVertical
- Move camera vertically
Usage Examples
Basic Setup
// Initialize input handler with contexts
UInputHandler* InputHandler = CreateInputHandler();
InputHandler->SetPlacementMappingContext(PlacementMappingContext);
InputHandler->SetToggleMappingContext(ToggleMappingContext);
// Enable toggle functionality
APlayerController* PC = GetWorld()->GetFirstPlayerController();
InputHandler->EnableToggleInput(PC);
Mode Transitions
// Enter placement mode
void EnterPlacementMode()
{
if (InputHandler && PlayerController)
{
InputHandler->EnablePlacementInput(PlayerController);
UE_LOG(LogTemp, Log, TEXT("Placement input enabled"));
}
}
// Exit placement mode
void ExitPlacementMode()
{
if (InputHandler && PlayerController)
{
InputHandler->DisablePlacementInput(PlayerController);
UE_LOG(LogTemp, Log, TEXT("Placement input disabled"));
}
}
State Validation
// Check input state during debugging
void ValidateInputState()
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = PlayerController->GetLocalPlayer()->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
// Check if toggle context is active
bool bToggleActive = Subsystem->HasMappingContext(ToggleMappingContext);
// Check if placement context is active
bool bPlacementActive = Subsystem->HasMappingContext(PlacementMappingContext);
UE_LOG(LogTemp, Log, TEXT("Toggle Input: %s, Placement Input: %s"),
bToggleActive ? TEXT("Active") : TEXT("Inactive"),
bPlacementActive ? TEXT("Active") : TEXT("Inactive"));
}
}
Priority System
InputHandler uses a priority-based system to ensure proper input handling:
Context | Priority | Purpose |
---|---|---|
Toggle Context | 5 | Base level - always available when system is active |
Placement Context | 10 | Higher priority - overrides conflicting inputs during placement |
This priority system ensures that:
- Toggle functionality is always available
- Placement controls take precedence when in placement mode
- No input conflicts occur between contexts
- Clean transitions between input states
Error Handling
InputHandler includes robust null-checking and validation:
void SafeEnablePlacementInput(APlayerController* PC)
{
if (!PC)
{
UE_LOG(LogTemp, Warning, TEXT("InputHandler: PlayerController is null"));
return;
}
if (!PlacementMappingContext)
{
UE_LOG(LogTemp, Error, TEXT("InputHandler: PlacementMappingContext is null"));
return;
}
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = PC->GetLocalPlayer()->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
Subsystem->AddMappingContext(PlacementMappingContext, 10);
UE_LOG(LogTemp, Log, TEXT("InputHandler: Placement input enabled"));
}
else
{
UE_LOG(LogTemp, Error, TEXT("InputHandler: Enhanced Input Subsystem not found"));
}
}
Performance Considerations
Efficient Context Management
- Minimal Overhead: Only activates/deactivates contexts when needed
- No Polling: Uses event-driven context switching
- Clean State: Properly removes contexts to prevent memory leaks
Best Practices
// Cache player controller reference to avoid repeated lookups
class UMyInputManager
{
UPROPERTY()
APlayerController* CachedPlayerController = nullptr;
public:
void Initialize()
{
CachedPlayerController = GetWorld()->GetFirstPlayerController();
}
void TogglePlacementInput(bool bEnable)
{
if (!CachedPlayerController) return;
if (bEnable)
{
InputHandler->EnablePlacementInput(CachedPlayerController);
}
else
{
InputHandler->DisablePlacementInput(CachedPlayerController);
}
}
};
Troubleshooting
Common Issues
"Input not responding"
- Verify Enhanced Input subsystem is available
- Check that mapping contexts are properly assigned
- Ensure PlayerController is valid
"Toggle still works in placement mode"
- This is expected behavior - toggle context remains active
- Placement context has higher priority but doesn't disable toggle
"Input conflicts between modes"
- Check priority values (placement should be higher than toggle)
- Verify input actions don't have conflicting key bindings
- Ensure proper context activation order
Debug Commands
// Debug input context state
void DebugInputState()
{
if (!PlayerController) return;
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = PlayerController->GetLocalPlayer()->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
// Get all active contexts
TArray<FEnhancedInputMappingContextRef> ActiveContexts = Subsystem->GetActiveMappingContexts();
UE_LOG(LogTemp, Log, TEXT("=== Active Input Contexts ==="));
for (const FEnhancedInputMappingContextRef& ContextRef : ActiveContexts)
{
if (ContextRef.MappingContext)
{
UE_LOG(LogTemp, Log, TEXT("Context: %s, Priority: %d"),
*ContextRef.MappingContext->GetName(),
ContextRef.Priority);
}
}
}
}
InputHandler provides a clean, reliable interface for managing Enhanced Input contexts, ensuring smooth transitions between input states and proper separation of placement and toggle functionality.