Seeing as there is very little information on the controller class and how it works, I wanted to basically break down my understanding of its implementation so that others are more well informed in the structure of its characteristics. I always found it hard to visualize what is going on when courses tell you to simply do xyz and really you’re just copying but not understanding what’s going on internally. Mostly I’m doing this for myself because I want to be able to refer to my notes should I need them.
So first thing to understand is the prefixes behind the class hierarchy of Unreal Engine, in this case you would already know the breakdown differences between actors and unreal engine objects, and their naming schema. In the case of an actor, in c++ you would reference an an actor by prefixing what you want with the A nomenclature, for example AController, APawn because these are actors. In the case of Unreal Engine objects, likewise, you would prefix it with U, so it would look like UPlayerInput, UAnimationAsset, and so on. F and T in that regard stand for float, and template.
With this understanding in mind, now it’s worthwhile to begin explaining the structure behind the controller class, and how to best visualize what’s going on; and in this example I’ll use code from the ThirdPerson Template.
First let’s start off by breaking down the UE Documentation, you’ll notice that the controller class inherits from AActor, and AActor inherits from UObject. UObject is the base class for all objects in unreal engine. You’ll notice also that AController has two children respectively APlayerController and AAIController.
And as you already guessed APlayerController is what is used for players meaning you, me, and anyone playing the game, and AAIController is used by NPCs within the game.
Before thinking about the ThirdPerson template you probably guessed already that because Pawns can be controlled by the player, that they by default have an implemented APlayerController, and this is true:
Inside Pawn.h:
The APlayerController essentially “becomes the AController” that you pass inputs to if you are the player. Within the APlayerController you have a UObject that sets the mappings for the inputs passed into the controller and that object is UPlayerInput. To put it shortly, when you enter into your project settings and manually set the input mappings for the actions and axis of your game, you are modifying the UPlayerInput object of all APlayerControllers inside your game:
Now the UPlayerInput object is only responsible for setting the mappings and not binding them. Understanding this allows you to learn that you can also set the mappings individually for any ACharacter you create, and you can do this using C++:
// Directional movement mappings
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping(FName(TEXT("XMovement")), EKeys::W, 1.0f));
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping(FName(TEXT("XMovement")), EKeys::S, -1.0f));
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping(FName(TEXT("YMovement")), EKeys::D, 1.0f));
UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping(FName(TEXT("YMovement")), EKeys::A, -1.0f));
With this in your character class now the mapping is relative to just your own created class. I’ve always wondered what people meant when they said you can have more control using C++ instead of defaulting to only blueprints, and when I learned about this I began to understand what they meant.
Anyway, back to the ThirdPerson template, I will assume you already know how to bind the Axis to a function using blueprints or c++ code so I’ll skip that portion, if you don’t already know, it’s worthwhile to look up on YouTube or any other guide out there.
According to the documentation’s definition:
“Controllers are non-physical actors that can possess a Pawn to control its actions.”
And personally I find it better to draw it out as if it were a physical representation. When it comes to AController, it is easy to envision it as a Marrionette:
And on the top you have wires extending from a plane that controls the figure. In the case of AController being used in the Third Person template, you can picture the figure as being the Pawn for ACharacter, and the plane as the APlayerController.
When you call GetController() in your code you are essentially getting the plane on the Marionette. This plane on the Marionette will have a rotation to it, and rotating it also rotates the Pawn along with it because the plane controls the orientation of the way the figure is pointing in a given space.
Now with that in mind you can use the orientation of this plane by adjusting its yaw, pitch, and roll. When you do this, the figure will also orient itself accordingly. In the same manner, you can visualize what is going on in the character.cpp file inside the Third Person template when a method like AddControlYawInput() is called:
void ATPProjectCharacter::TurnAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}
In here the rate which is being passed in is simply the axis value we got based on the binding we set for the controller. But why are we multiplying this value by the DeltaSeconds (if you don’t know what this is, it is the time between frames) and the BaseTurnRate? Well this is answered by physics.
Imagine the plane we talked about earlier has now transformed into a sphere with a yaw component, a roll component, and pitch component. Cut out a circular plane that makes up its yaw component. If you took calculus/physics in school you would know about angular velocity, and thus the concept of the final distance traveled within the circumference of the circle explains the use of BaseTurnRate and DeltaSeconds:
I hope you can forgive my poor drawing skills, but this is essentially the breakdown for what is going on in that part of the implementation. If you just turn by the rate value alone, alone then the controller rotation will update faster than what your GPU can handle, because you have to remember that the cpu will always work faster to perform calculations than your GPU is able to process the output into an frame for said calculation.
Lastly when you Unposses() a controller, you basically snip the wires off the marionette.
This should be enough to get you, and me (if I ever forget how to use UE), started on better understanding how to do more things with the controller.