Grid Placement System
Library Components
InputHandler

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:

  1. Toggle Context - Always active when placement system is available

    • Contains only the placement mode toggle action
    • Remains active in both Normal and Placement modes
  2. 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 placement
  • IA_PlacementCancel - Cancel placement or exit mode
  • IA_PlacementRotate - Rotate preview object
  • IA_PlacementZoom - Zoom camera in/out
  • IA_PlacementMove - Move camera
  • IA_PlacementShift - Hold for precision mode
  • IA_SelectObject - Select placed objects for editing
  • IA_DeleteObject - Delete selected objects
  • IA_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:

ContextPriorityPurpose
Toggle Context5Base level - always available when system is active
Placement Context10Higher 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

  1. Minimal Overhead: Only activates/deactivates contexts when needed
  2. No Polling: Uses event-driven context switching
  3. 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.