In this project, I wanted to create a prototype for a 2D multiplayer twin-stick shooter - drawing inspiration from games like Enter the Gungeon. My development environment included Godot 4.2 for the game client and Nakama for the server-side logic.
For a deeper dive into the project, feel free to explore the YouTube video and check out the source code on GitHub here: engine-sandbox-golang
Watch the Prototype in Action
Before diving into the technical aspects, check out the video below, where you can see four separate game clients interacting with the same Nakama server. Each client runs on the same machine, and clicking on any window brings it into focus, enabling input for that specific client:
Core Features
Multiplayer Setup
Using Nakama as the game server, this project supports multiple clients connected to a shared game state.
-
Client Focus Control: Each client listens for input only when in focus. This ensures that keystrokes and mouse clicks are captured correctly without interference from other clients running on the same machine.
-
Cursor and Rotation: When a client is in focus, the mouse cursor is replaced with a green circle indicating where the player will fire. The player sprite rotates to always face the reticle. This rotation information is synchronized with the Nakama server, allowing all players to see each other’s orientations.
-
Shooting: Clicking anywhere on the map fires a green projectile towards the reticle. The movement of players and projectiles is synchronized across all clients.
-
Movement: Players can navigate the map using the WASD keys, and both players and projectiles are networked game objects.
Network Communication
The Nakama server handles network operations by receiving predefined opcodes from game clients and broadcasting them to all other authenticated clients in the match. Here’s how position updates are sent from a client to the server:
When the Nakama server receives this message it authenticates the sender and relays the message to all other clients. Each client handles the incoming network messages with a match statement that switches based on the opcode:
Opcode Definitions
Each opcode is assigned a unique integer constant shared across all game clients: