top of page

Breaking Down Unreal

Basics of Input in Unreal

  • Jan 12, 2022
  • 4 min read

Input in Unreal is a complicated thing if you don't understand it. It can feel like there are so many layers and intricacies that make managing it feel like a nightmare. This feeling drives many to placing all of their controls in one or a couple of places.


Lets start with some basics. Most know after just a few tutorials that you can put input in your Player Controller or Player Pawn. But how does that really work? There will be some C++ here, but don't worry blueprinters, I'll do my best to simplify it.


To cut in briefly before we start. There are three input modes. GameOnly, GameAndUI, and UIOnly. The following segments in this guide will help make much more sense of these three settings. But to briefly note, GameOnly controls will ignore all UI overrides and only work from InputComponents. UIOnly is the opposite, no input components are considered even after input runs through UI. And GameAndUI allows input to go through the entire tree of controls for both UI and InputComponents. For the purposes of this post, we'll assume GameAndUI is set.




The very top of controls

Lets start at the very top. FSlateApplication. This has, among other things, the responsibility to dispatch key events directly to any slate widget.


This image is from FSlateApplication::ProcessKeyCharEvent

Even Blueprinters who use UUserWidgets will recognize the two circled names.



These are the C++ overrides for UUserWidget's two.


These are the blueprint versions that are called in the default UUserWidget class.



If you're still with me, the idea here is that UWidgets(And UUserWidget) are sent these dispatches. If they return unhandled, it's sent to the next widget and the next. This chain immediately breaks the moment that one widget returns Handled. The interesting thing here is that OnPreviewKeyDown and OnKeyDown traverse the tree in separate directions.


For example, lets say that we have three separate UserWidgets. Their contents don't matter. We enable focusable on them and nest them one in the other. Our hierarchy is as follows.

UserWidget1->UserWidget2->UserWidget3


So UserWidget3 is inside of UserWidget2, which is inside of UserWidget1. If we set focus to the bottom most point in our hierarchy, UserWidget3, you will notice the following order of calls to your PreviewKeyDown and KeyDown events. UserWidget1 - > PreviewKeyDown

UserWidget2 - > PreviewKeyDown

UserWidget3 - > PreviewKeyDown

UserWidget3 - > KeyDown

UserWidget2 - > KeyDown

UserWidget1 - > KeyDown


This hierarchy traversal allows you to normally use the correct outermost widget that has focus first in most normal situations on KeyDown. Though if you had a key that you absolutely wanted overridden in the focus path, you could use PreviewKeyDown instead.




But why do we care about UI controls?

Well, learning about UI controls is great and all, but where does that leave us with normal gameplay controls? Also why does it matter that we know this about UI? The reason is simple. If a key is returned Handled from any widget that has focus, the key will never make it to your InputComponents!. This means that UI can entirely override game UI.


This is an important fact to know if you ever try to plop an input in and can't get it to run, even directly in your player controller. If other controls are running but not that one then chances are someone handled it in a widget override.




On to game inputs!

Most gameplay programmers are already aware of how to enable inputs. Largely this is all the same with the added step in C++ that you need to override a function manually to add input bindings that call your events, while dropping inputs into the blueprint graph does this for you.


As far as people new to the engine are aware, only three things seem to have input and you may not even be aware of all three. Those by default are the Level, PlayerController, and whatever Pawn the PlayerController is possessing.


To give a brief overview before I dive into the inner workings. I will just point out that every Actor has the ability to create it's own InputComponent. This component can be sent to the PlayerController which will then put it on it's input stack. The InputStack is basically a list of InputComponents that the PlayerController has registered.


So where do we begin to understand player controller input stack processing?

Lets take a quick look at the function that actually processes input.


This is the ProcessPlayerInput on the PlayerController. You don't need to be a C++ expert or even user at all to sort of follow this.


First the input stack is created as an array of InputComponent pointers. Later that array is sent into a function that populates it named BuildInputStack. And after that the stackarray is sent to a function that iterates over this newly built stack to send out the inputs that were processed. After that the input stack is reset.


ProcessInputStack is less important, it's just simply a function that doles out all of the events or bindings for Action and Axis mappings. It is BuildInputStack that is much more important for understanding.



I have purposely hidden away all of the complex code to get a clear view of how this function handles. First the Pawn that the PlayerController is possessing is added. After that the LevelBlueprint, then the PlayerController's own InputComponent, and last are any other actors that you have ever called EnableInput on and never DisabledInput on.


The important thing to know is that this list is backwards. This means that events will be sent out to any InputEnabled actors first, and then the PlayerController, then the Level and then the possessed Pawn.


For a simpler preview, that order is as follows from top to bottom, assuming you have enabled input in two other actors.


LastEnabledActor

FirstEnabledActor

PlayerController

LevelBlueprint

PossessedPawn


What this means i that when you have an action mapping in the PlayerController, and the same exact action mapping in the Pawn, the controller will receive it first. This means that if the controller consumes input, the pawn's action mapping for that will never run.


This behavior is often useful in the sense of InputEnabled actors and the Pawn. You may have an action in the Pawn that does something when you press "E". But then you walk into an actor's trigger, and enable input on it. This actor uses "E" as well and consumes it's input. Now this new actor can run something when "E" is pressed and the pawn will not run it's action mapping.




Thank you for reading!

 
 
 

Recent Posts

See All
UObject::Serialize

The Serialize function from the UObject class is a key feature in the engine that surprisingly few people know about. It isn't talked...

 
 
 
Saving Games Smartly

A cornerstone to games is the ability to save and load them. This is true whether you use a simple checkpoint system or a full blown full...

 
 
 

Comments


bottom of page