Skip to main content

đŸ•šī¸ 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 OnPossess and 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 possessedMesh, collision, movement
Player-facing UI / HUDBody-specific abilities & stats
Input mode (game vs UI), mouse cursorAnimation state
Camera management (can override)The camera components themselves
Persistent-per-session player logicEverything 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.

graph LR PC["APlayerController"] -->|Possess pawn| P1["Pawn A"] PC -.UnPossess.-> P1 PC -->|Possess another| P2["Pawn B"]
Figure 1: A controller possesses one pawn at a time. Un-possessing A and possessing B is exactly how you make a player leave one body and enter another.
// 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.

  1. The pause menu widget
  2. The character's current ammo
  3. Whether the mouse cursor is shown
  4. The ragdoll physics on death
  5. 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(); then Possess(Vehicle); — and Possess(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 (call Super) for per-pawn setup; it's a clean home for the Enhanced Input mapping context, while bindings stay in the pawn's SetupPlayerInputComponent.
  • 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.