fine to answer you vague open ended question more specifically
Q: “How to remove/swap Input Mapping Context at runtine”
-after 10-20 minutes of playing around with Intellisense
in a C++ project (blueprint projects made in 5.1.1 or older will need to add EnhancedInput to modules and/or add-ons as needed)
created some Input Actions, and an Input Mapping Context assigning them as such
then in the C++ Character class header “InputsCharacter.h”
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
#include "InputsCharacter.generated.h"
UENUM(BlueprintType)
enum class EControllingState : uint8
{
ECS_General = 0 UMETA(DisplayName=General),
ECS_Driving = 1 UMETA(DisplayName=Driving)
};
UCLASS(config=Game)
class AInputsCharacter : public ACharacter
{
GENERATED_BODY()
/** Camera boom positioning the camera behind the character */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class USpringArmComponent* CameraBoom;
/** Follow camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* FollowCamera;
/** MappingContext */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputMappingContext* DefaultMappingContext;
// Begin Other Mapping Context
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputMappingContext* DrivingMappingContext;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
EControllingState CurrentControlState;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* AccelAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* TurnAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* BreakAction;
// End Other Mapping Context
/* Shared between both*/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* ChangeInputAction;
/** Jump Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* JumpAction;
/** Move Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* MoveAction;
/** Look Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* LookAction;
public:
AInputsCharacter();
protected:
void ChangeInput(EControllingState TargetScheme);
/** Called for movement input */
void Move(const FInputActionValue& Value);
/** Called for looking input */
void Look(const FInputActionValue& Value);
// added for demonstation
void SwapInput(const FInputActionValue& Value);
void TurnVehicle(const FInputActionValue& Value);
void AccelVehicle(const FInputActionValue& Value);
void DeccelVehicle(const FInputActionValue& Value);
protected:
// APawn interface
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// To add mapping context
virtual void BeginPlay();
public:
/** Returns CameraBoom subobject **/
FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
/** Returns FollowCamera subobject **/
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
};
then in the cpp added (will start at the end of the default constructor)
// ... Class constructor above this
CurrentControlState = EControllingState::ECS_General;
}
void AInputsCharacter::ChangeInput(EControllingState TargetScheme)
{
//Add Input Mapping Context
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
// Mapping Context to remove
UInputMappingContext* tempCurrentMappingContext = nullptr;
switch ( CurrentControlState )
{
case EControllingState::ECS_Driving:
tempCurrentMappingContext = DrivingMappingContext;
break;
default:
tempCurrentMappingContext = DefaultMappingContext;
break;
}
switch ( TargetScheme )
{
case EControllingState::ECS_Driving:
Subsystem->RemoveMappingContext(tempCurrentMappingContext);
Subsystem->AddMappingContext(DrivingMappingContext, 0);
CurrentControlState = EControllingState::ECS_Driving;
break;
default:
// Remove the current Mapping Context to prevent conflicts
// it is just a pointer after all, UBT does complain if you try to use nullptr
// it is safe to remove something that isn't registered as it will be ignored
Subsystem->RemoveMappingContext(tempCurrentMappingContext);
// Add the new Mapping Context
Subsystem->AddMappingContext(DefaultMappingContext, 0);
// change Enum state to prevent dumb bugs
CurrentControlState = EControllingState::ECS_General;
break;
}
}
}
}
void AInputsCharacter::BeginPlay()
{
// Call the base class
Super::BeginPlay();
// Apply Mapping Context
ChangeInput(CurrentControlState);
}
//////////////////////////////////////////////////////////////////////////
// Input
void AInputsCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
// Set up action bindings
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent)) {
EnhancedInputComponent->BindAction(AccelAction, ETriggerEvent::Triggered, this, &AInputsCharacter::AccelVehicle);
EnhancedInputComponent->BindAction(BreakAction, ETriggerEvent::Triggered, this, &AInputsCharacter::DeccelVehicle);
EnhancedInputComponent->BindAction(TurnAction, ETriggerEvent::Triggered, this, &AInputsCharacter::TurnVehicle);
EnhancedInputComponent->BindAction(ChangeInputAction, ETriggerEvent::Triggered, this, &AInputsCharacter::SwapInput);
//Jumping
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
//Moving
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AInputsCharacter::Move);
//Looking
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AInputsCharacter::Look);
}
}
void AInputsCharacter::SwapInput(const FInputActionValue& Value)
{
switch ( CurrentControlState )
{
case EControllingState::ECS_General:
ChangeInput(EControllingState::ECS_Driving);
break;
default:
ChangeInput(EControllingState::ECS_General);
break;
}
}
// Other class functions (leaving implementation up to reader)
then assigning the Mapping Context, and Input Actions in the class Blueprint to avoid hard PathString references (don’t want someone moving or renaming something making a runtime resolution bug and breaking our program)
character is able to move around while DefaultMappingContext is applied and when hitting the ChangeInputAction button logically becomes a Car (model does not change)
Even though the ActionBindings are not removed from the EnhancedInputComponent they are not processed if they are not in the Current Mapping Context, and the only value I can see in removing them at runtime is if you were to destroy their respective Mapping Context
for Unbinding what is effectively a Delegate from the EnhancedInputComponent
the closest was needing to get the BindingIndex
but I couldn’t figure that out in the time I spent just playing with the system.
The most charitable I can be about this is: “Now we both learned something”