Enhanced Input bindings on Controller, scalability

I’ve been searching for answers, but most of them are either outdated (eg. does not use the Enhanced Input system) or suggest a solution that I cannot agree with.

So my main goal is to separate the controllable pawns and the control logic (i.e. input handling). This is done by mapping input actions to functions in the player controller (and adding input mapping contexts accordingly).

This works fine until there are a small number of controllable pawns. Currently, the player’s movement (move left-right and jump, atm) is listened by the controller and the controlled pawn’s functions are called directly.

However, this solution is not scalable. What happens when I’d like to introduce a new object in the world that can be controlled but with a completely different scheme? Of course I can:

  • Create new input actions
  • Add them to the player controller
  • Register input action handlers in the controller
  • Bound appropriate input mapping context in the OnPossess function
  • Call methods directly on the downcasted possessed pawn

There are multiple problems with this solution:

  • Need to branch in the OnPossess function to determine which input mapping contexts should be added and/or removed
  • Need to contain all possible input actions and their handlers (can be a good thing tho)
  • Need to downcast for the specific type of the possessed pawn if there is a special logic that must be executed when the given input is handled

Is there any appropriate and modern way to do input handling in the controlled while keeping it scalable, so it’s not a pain to introduce a new controllable pawn?

Somewhat related posts:

1 Like

im not sure if this is what you mean but you could

you could put the controls on an actor component which then calls event dispatchers on the owner actor?

or keep them on the player controller an use an interface to send messages to the possessed actor

1 Like

Yea, both solution would work fine, and I mostly done it with the 2nd approach with the big difference that I directly call functions on the possessed pawn instead of using an interface.

Using an actor component would defeat the purpose of the player controller. In my perspective, the actor itself (pawn in this case) is the 2D/3D representation of the currently controlled entity, while the player controller is the source of intent / player’s will.

However, if there are multiple types of entities that can be controlled in different ways (standard player character, vehicles, flying objects, controllable missile created by an ability, etc.) the player controller would become really messy with handling all possible types of inputs, which does not scale very well.

I wouldn’t really like to put input handling on the actor itself, because of the separation of concerns. Additionally, I’d like to future-proofing the core, thinking on multiplayer (either local multiplayer or standard server-client) and scalability.

As an example, if I put every input relevant code on the player controller, it can become messy:

// has to declare the input action
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input", meta = (AllowPrivateAccess = "true"))
UInputAction* InputActionMove;

// has to declare an input mapping context if needed
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input", meta = (AllowPrivateAccess = "true"))
UInputMappingContext* MovementInputMappingContext;

Also, in the implementation:

// has to bind all possible input action to something
if (InputActionMove && ensure(InputActionMove->ValueType == EInputActionValueType::Axis1D)) {
	Input->BindAction(InputActionMove, ETriggerEvent::Triggered, this, &AMyPlayerController::Move);		
}

// and in the OnPossess function, the appropriate context needs to be added
if (IsMovementRelated()) {
	EnhancedInput->AddMappingContext(MovementInputMappingContext, 0);
	// and potentially remove other mapping contexts
}

In contrast, the pawn itself could contain the input actions, input mapping context(s) and within its own PossessedBy function, it could add the input mapping context to the player. However, I think it would become more difficult to manage which input mapping contexts are active (to avoid conflicts), and the whole input handling should not be on the actor, because it’s a human-related thing and the actor should not care about it.

well the point of the actor component is you could use a different component for say character, vehicle, flying etc which is extendable and modular

but id lean towards the interface, so your input can be generic like moveforward which calls the movefoward event on the pawn and in the pawn you handle the separate ways to move such as walking, flying etc.

or you can do both, have generic movement on the player controller which calls an actor component which handles different movement modes

And does this approach fit well with multiplayer or AI controlled characters?

My problem with the interface approach is that for example “move up” and “move down” only makes sense for a flying vehicle, and maybe the “move” itself makes no sense if, for example an ability fires a rocket that is possessed by the player and can be controlled with the mouse (but not moved).

What do you think about a mixed approach? The input handlers would live on the controlled actor, but the input mapping context (which binds the input(s) to the action) would be added by the player controller. However, I don’t think that this works for a split-screen multiplayer (or even for any multiplayer at all). The same input action (e.g. IA_Move) would be triggered for each controlled actor, so if player 1 tries to move forward, it would move forward all characters, which is far from ideal. :slight_smile:

move up/down would just be ignored in actors that dont use it, ie and empty event that does nothing.

its fine for multiplayer because even though its the same context/IA its on a different controller

I cannot seem to figure out better way to do it, currently. I have to know the “superset” of available inputs anyway and it’s not worse than downcasting to the actual type.

Additionally, it’s possible to introduce multiple interfaces, even for each input mapping contexts (e.g. IFlyingVehicleController or IHumanoidCharacterController). With this separation the controller could automatically add the appropriate input mapping contexts based on which interface the possessed actor implements.

I’ll accept your answer as a solution, but I’m still open to any other ideas. Thanks for your time.

yeah you could have 2 different mapping contexts, say one for character, one for vehicle. this would just mean you have duplicates as in each one would have a move forward but thats no big deal and allows players to change their controls if they want to, for instance they might want W for character move and uparrow for vehicle move.

then just add/remove context based on what you have possessed

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.