Removing Input Contexts when Using the Enhanced Input System

The documentation still leaves a lot to be desired with regards to the enhanced input system considering they deprecated the old input system.

Does anyone have an example of how to remove an input context and their associated action bindings? The documentation says you can do it, but doesn’t provide an example.

what do you mean by remove?
these are just pointers and references.
to remove an InputAction from an InputMappingContext you hit the little ‘garbage can’
to remove an InputMappingContext you unassigne it in the blueprint.

what is the specific thing you want to do, and are you asking about in Blueprints or C++?

What I mean is, the documentation should show an example for the opposite of this:

if (ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(Player))
{
    if (UEnhancedInputLocalPlayerSubsystem* InputSystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
    {
        if (!InputMapping.IsNull())
        {
            InputSystem->AddMappingContext(InputMapping.LoadSynchronous(), Priority);
        }
    }
}

… as well as removing the action bindings that were made in the first place.

even implementing Enhanced Input Subsystem in C++ still required some secondary steps in blueprints, unless you are assigning them by PathString which is resolved at runtime, so for flexibility and consistency some Blueprint stuff is still required.

in C++ you can just set the pointer to nullptr to outright disable it, or assign it to something else (having 2 InputMappingContext* to swap when you want or to have multiple InputActions do the same thing)

Anything beyond this to accomplish a specific effect would need more detail.

Thanks but you have no idea what you’re talking about. Please stop posting.

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”

The function is simply called “Remove Mapping Context” you might have to uncheck “context sensitive” to be able to see it. This drove me mad for an hours just now until I could find it because of that.

1 Like

the desire was to handle the mapping contexts in C++, and I had asked for if this question was for Blueprints, which is why my post directly above yours is in C++.

realistically it is doable to do remove->add in Blueprints and C++, but for consistency reasons unless there is specific/unique reasons to do remove->add of a thing that is being handled in C++ it should not also be modified in blueprints.

if your project is using C++; then the C++ should be used for the general large moving pieces stuff, and blueprints should mostly stick to the hyper-specific stuff. For example it is doable to do a controllers full 16 unique InputActions in C++, but then add in blueprint InputActions for the number-pad or F-Row for debug, or release mode console things in addition to the C++ stuff.

the concern is if a system is expected by the C++ to be handled in the C++, and then it is modified in Blueprint there is a good chance the program gets into an unrecoverable state (especially if say the default MappingContext is not re-assigned and just removed)