đšī¸ Lesson 4.3: PlayerController & Possession
The Pawn is the body; the PlayerController is the brain. Understanding what belongs on which â and how a controller possesses a pawn â resolves a surprising number of "where does this code go?" questions and unlocks mechanics like swapping bodies.
đ¯ Learning Objectives
By the end of this lesson, you will be able to:
- State what the PlayerController owns vs what the Pawn owns
- Explain possession and use
Possess/UnPossess - Override
OnPossessand choose where to set up input - Get from controller to pawn and back (
GetPawn,GetController) - Implement swapping which pawn a player controls
Estimated Time: 45 minutes
Engine Version: Unreal Engine 5.8
In This Lesson
Who Owns What
The Pawn/Controller split (Lesson 3.1) becomes practical here. As a rule: things tied to the body live on the Pawn; things tied to the player live on the Controller.
| PlayerController (the player) | Pawn/Character (the body) |
|---|---|
| Which pawn is possessed | Mesh, collision, movement |
| Player-facing UI / HUD | Body-specific abilities & stats |
| Input mode (game vs UI), mouse cursor | Animation state |
| Camera management (can override) | The camera components themselves |
| Persistent-per-session player logic | Everything that dies with the body |
đ The durability test
Ask: "should this survive the body dying and respawning?" If yes (score, which team, UI), it belongs on the Controller (or PlayerState for replicated data â Lesson 3.1). If it dies with the body (health, ammo in the gun, ragdoll), it belongs on the Pawn. Same durability principle as Pawn-vs-PlayerState.
Possession
Possession is the act of a controller taking control of a pawn. When a controller possesses a pawn, input routes to it, the controller's rotation drives the pawn's aim, and the two are linked until UnPossess.
// From the controller, take control of a pawn:
void AMyPlayerController::TakeControlOf(APawn* NewPawn)
{
if (!NewPawn) { return; }
UnPossess(); // release the current pawn (if any)
Possess(NewPawn); // possess the new one â input now routes here
}
â ī¸ Possession is server-authoritative
In multiplayer, Possess must be called on the server â it's an authority operation, and the result replicates to clients. Calling it on a client does nothing meaningful. We'll revisit authority in Module 10; for single-player it "just works," but build the habit of thinking of possession as a server action.
OnPossess & Input Setup
The controller's OnPossess fires when it takes a pawn â a reliable hook for per-pawn setup. This is also the cleanest place to add the Enhanced Input mapping context (an alternative to the pawn's BeginPlay from Lesson 4.1), because the controller is guaranteed to exist and own the local player here.
#include "EnhancedInputSubsystems.h"
void AMyPlayerController::OnPossess(APawn* InPawn)
{
Super::OnPossess(InPawn); // always call Super â it does the real possession work
// Add the mapping context here: the controller + local player are ready.
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
UE_LOG(LogTemp, Log, TEXT("Now possessing %s"), *InPawn->GetName());
}
đ Two valid homes for input setup
Adding the mapping context in the pawn's BeginPlay (Lesson 4.1) is common and fine. Doing it in the controller's OnPossess is arguably cleaner: context lives with the player, and it re-applies correctly whenever the player takes a new body. Either works â just be consistent. The action bindings stay in the pawn's SetupPlayerInputComponent regardless.
â Remember EndPlay / UnPossess cleanup
If you add a context on possess, consider removing it on OnUnPossess so a swapped-away body doesn't leave stale mappings. Symmetric setup/teardown (Lesson 2.6's EndPlay discipline) keeps behavior predictable when bodies change.
Swapping Pawns
Here's the payoff of the separation: because control is possession, "leave your character and take over the turret" is just an unpossess-then-possess. The player's identity (Controller, PlayerState) stays put; only the body changes.
// Player presses "interact" near a turret; the turret asks the controller to swap.
void AMyPlayerController::EnterTurret(APawn* Turret)
{
// Remember the body we're leaving so we can return to it later.
PreviousPawn = GetPawn(); // UPROPERTY() or TWeakObjectPtr â GC-safe (Lesson 2.5)
Possess(Turret); // UnPossess of the old happens automatically on Possess
}
void AMyPlayerController::ExitTurret()
{
if (IsValid(PreviousPawn))
{
Possess(PreviousPawn); // hop back into the original body
}
}
â Why this is elegant
No copying of score, no re-creating UI, no losing player identity â all of that lives on the durable Controller/PlayerState, untouched by the body swap. This is the concrete reward for the "body and brain are separate" design you learned back in Lesson 3.1. Store PreviousPawn as a UPROPERTY/TWeakObjectPtr so the GC doesn't collect the body you plan to return to.
Hands-on Exercise & Quiz
đī¸ Exercise: Place the responsibilities
Objective: Decide Controller vs Pawn for each, then sketch a possession swap.
- The pause menu widget
- The character's current ammo
- Whether the mouse cursor is shown
- The ragdoll physics on death
- Write the two calls that move the player from their character into a nearby vehicle pawn and store the way back.
â Answers
- 1. Controller (player-facing UI).
- 2. Pawn (dies with the body).
- 3. Controller (input mode / cursor).
- 4. Pawn (body-specific).
- 5.
PreviousPawn = GetPawn();thenPossess(Vehicle);â andPossess(PreviousPawn)to return.
đ¯ Quick Quiz
Question 1: Where should a player's HUD/UI logic live?
Question 2: Which pair swaps a player from one body to another?
Question 3: A pawn's GetController() returns null. What's a valid reason?
Summary
đ Key Takeaways
- Controller = player (UI, input mode, which pawn, durable logic); Pawn = body (mesh, movement, body stats). Use the "does it survive respawn?" test.
- Possession links a controller to a pawn:
Possess/UnPossess; it's a server-authoritative operation. - Override
OnPossess(callSuper) for per-pawn setup; it's a clean home for the Enhanced Input mapping context, while bindings stay in the pawn'sSetupPlayerInputComponent. - Navigate with
GetPawn/GetController/GetPlayerState<T>â all nullable, always checked. - Swapping bodies is just
Possess-ing another pawn; player identity on the Controller/PlayerState stays intact (store the return body GC-safely).
đ Additional Resources
đ What's Next?
Module 4 is complete â you have a controllable player, wired from key to movement to possession. Module 5 makes the world react: collision and overlaps, traces, timers, delegates, and interfaces â the interaction toolkit.
đ Module 4 complete!
A player drives your game. Now let's make the world push back.