Skip to content

Create Local Player / Get Num Player Controllers

At a glance

Lives in: Blueprint API / Game; implemented by Gameplay Statics and GameInstance - Returns: Create Local Player returns the new PlayerController if one is created; count nodes return integers - Fails by: returning None or a count that means something different on server, client, or dedicated server - Official docs: Create Local Player, Get Num Player Controllers, Get Num Local Player Controllers, and Local Multiplayer

The one-minute version

  • A local player is a player using this machine: keyboard/gamepad/split screen on this process.
  • Create Local Player adds another local player, usually for couch multiplayer. If Spawn Player Controller is true, it can return the created PlayerController.
  • Local multiplayer is not network multiplayer. Two local players can share one machine; network clients are separate machines/processes.
  • Get Num Player Controllers counts available PlayerControllers for the current machine's world, including remote controllers when called on the server.
  • Get Num Local Player Controllers counts fully initialized local players and is 0 on a dedicated server.
  • These nodes do not create remote clients, guarantee the same player index on every machine, or automatically give each local player UI and mapping contexts.

What they actually do

Think of local players as viewport/input slots inside one game instance:

flowchart LR
    GI[GameInstance on this process] --> LP0[LocalPlayer 0]
    GI --> LP1[LocalPlayer 1]
    LP0 --> PC0[PlayerController 0]
    LP1 --> PC1[PlayerController 1]

Create Local Player adds a ULocalPlayer to the current GameInstance. Blueprint exposes the practical result: if a PlayerController is spawned now, you get that controller reference back.

pseudocode of the engine behavior - not engine source

function CreateLocalPlayer(ControllerId, SpawnPlayerController):
    LocalPlayer = GameInstance.CreateLocalPlayer(ControllerId)
    if LocalPlayer could not be created:
        return None

    if SpawnPlayerController:
        return LocalPlayer.PlayerController

    return None

function GetNumLocalPlayerControllers():
    return count of fully initialized local players with controllers

function GetNumPlayerControllers():
    return count of available player controllers in this world

The difference matters most in multiplayer. On a dedicated server, there are no local players. On a listen server, the host has a local player and also sees server-side controller objects for connected players. On a client, remote PlayerControllers generally are not available.

When they fail (and what failure does)

Common causes:

  1. Local multiplayer is not enabled by design. Your game mode/UI/camera setup may assume one local player even if the node can create another.
  2. Spawn Player Controller is false. The node may add the local player but return None because the controller will be created later, often during map travel.
  3. Controller ID is unavailable or mismatched. -1 means "choose the next available ID"; explicit IDs can collide or not map to the device you expect.
  4. You used the wrong count. Get Num Player Controllers and Get Num Local Player Controllers answer different questions.
  5. The code ran on a dedicated server. Dedicated servers have no viewport and no local players.
  6. Widgets were created with the wrong owning player. Player 1's UI needs Player 1 as owner, not hard-coded player index 0.
  7. Enhanced Input contexts were added only for player 0. Each local player has its own local-player subsystem/mapping context setup.

Failure does not spawn fallback UI, remap devices, or make player index order stable across server and clients. You get a controller reference or None, and your setup graph must handle both.

The pattern everyone actually uses

For couch co-op from a menu:

Host selects Add Player
-> Create Local Player(Controller Id = -1, Spawn Player Controller = true)
-> Is Valid Return Value
-> create HUD with Owning Player = returned PlayerController
-> add gameplay mapping context for that local player's subsystem
-> let GameMode spawn/restart the pawn for that controller

For UI setup in split-screen:

For each local PlayerController
-> Create Widget WBP_PlayerHUD with Owning Player = that controller
-> Add to Player Screen / viewport path appropriate for local player

For network scoreboards, do not iterate PlayerControllers from a client. Use GameState's PlayerArray and PlayerStates instead.

The C++ twin, for the curious (our own example code):

void ULocalCoopMenu::AddLocalPlayer()
{
    FString Error;
    ULocalPlayer* LocalPlayer =
        GetGameInstance()->CreateLocalPlayer(-1, Error, true);

    if (!LocalPlayer || !LocalPlayer->PlayerController)
    {
        ShowAddPlayerError(Error);
        return;
    }

    CreateHudFor(LocalPlayer->PlayerController);
}

Player index traps

Player Index 0 is a local-machine convenience, not a universal identity:

  • In single-player, it usually feels like "the player."
  • In split-screen, player 0 and player 1 are both local, and each needs the right owning controller.
  • In network play, index order can differ between server and clients.
  • Remote clients usually cannot get other players' PlayerControllers.

Use PlayerState for public player data, PlayerController for local input/UI, and Pawn/Character for the current body.

Lookalikes - which one do I want?

Node / object Returns / changes Use when
Create Local Player Adds a local player; may return a new PlayerController Couch co-op / split-screen on one machine.
Remove Player Removes a local player A local split-screen player leaves.
Get Num Local Player Controllers Count of fully initialized local players You are iterating local input/UI owners.
Get Num Player Controllers Count of available controllers for this world/machine Server/listen-server code needs available controller count.
Get Player Controller(index) A controller from that machine's list, or None You need a specific local input/UI owner.
PlayerState / GameState PlayerArray Public replicated player records Scoreboards, names, teams, ready state.
Network multiplayer session travel Remote players join as separate machines/processes You are building online multiplayer, not couch co-op.

Rule of thumb: LocalPlayer is viewport/input on this machine; PlayerController owns input/UI; PlayerState is the public player record everyone can read.

Going deeper