Local co-op is having a moment, and Godot handles it well once you know the two pieces: telling two players apart on input, and showing each one their own view if you want split screen. Neither is hard. People just rarely explain them together.
Two players, one machine
The cleanest way to support multiple local players is to give each one their own set of input actions. In Project Settings, Input Map, make two sets:
p1_left,p1_right,p1_up,p1_down,p1_actionp2_left,p2_right,p2_up,p2_down,p2_action
Bind player one to WASD and player two to the arrow keys, then bind each set to a gamepad too. Now each player script just reads its own actions. Pass the player index in when you spawn them:
extends CharacterBody2D@export var player_index: int = 1@export var speed: float = 250.0var prefix: Stringfunc _ready() -> void: prefix = "p%d_" % player_index # "p1_" or "p2_"func _physics_process(_delta: float) -> void: var direction := Input.get_vector( prefix + "left", prefix + "right", prefix + "up", prefix + "down" ) velocity = direction * speed move_and_slide()One script, two players, no duplicated code. The same prefix trick works for the action button and anything else.
Handling multiple gamepads
Godot tags every input event with the controller it came from. If you'd rather assign a whole gamepad to a player than rely on action bindings, read event.device in _input and route accordingly:
func _input(event: InputEvent) -> void: if event is InputEventJoypadButton: print("Input came from controller ", event.device)Device 0 is the first pad, 1 is the second, and so on. For a couch co-op game where players grab whichever controller, this is how you keep them straight.
Split screen
If both players share one camera, you're done already. Split screen is for when each player needs their own view.
The setup uses viewports. You put two SubViewportContainer nodes side by side, each holding a SubViewport, and give each viewport its own Camera2D that follows one player. The trick that makes it actually work: both viewports need to render the same world. You do that by sharing the World2D between them, so player one and player two see the same level from two cameras instead of two separate copies of everything.
It's more wiring than a single-camera game, so don't reach for it unless your players can actually move far enough apart that one shared camera stops working. Plenty of great co-op games (think a single arena) never need split screen at all. Start shared, add split only when the design demands it.
A quick gotcha
When you spawn two players, make sure they don't share state by accident. If both read from the same global "player" reference or the same input actions, they'll move as one. The per-index prefix above is what keeps them independent. If both characters move together, that shared input is almost always why.
Build a real co-op game
Co-op is one of those things that's more fun to build than it looks, because every system you add has to handle two players at once. Our free Co-Op Crate Combustion quest walks you through a complete local co-op game across 13 chapters: two players, levers, exploding crates, the works. It's a finished game you can hand a friend, which is the whole point of co-op.
FAQ
How do I support two players on one keyboard in Godot?
Create separate input action sets for each player (like p1_left and p2_left), bind them to different keys, and have each player script read its own set. Passing a player index into the script and building the action names from it lets one script drive both players.
How do I tell which gamepad an input came from?
Every input event carries a device property. Read event.device in an _input function to know which controller fired it. Device 0 is the first gamepad, 1 the second, and so on, which lets you assign a whole controller to a specific player.
Do I need split screen for local co-op?
Only if players can move far enough apart that a single shared camera can't keep both on screen. Many co-op games take place in one arena and never need it. Start with a shared camera and add split screen later if the design actually requires it.