How To Make a Simple 2D Multiplayer Game In Godot

Sw33tBit
9 min readOct 28, 2022

--

Greetings to everyone, today I will show you how you can make a multiplayer game in Godot.

I will cover everything you need to create your own multiplayer.

You can check full source code here:

https://github.com/BitR13x/2DGodotMultiplayer

Keep in mind that my code isn’t perfect and can be definitely upgraded, but still it can save you some time.

Server-side vs Client-side

It’s important to know the difference.

When you understand it, everything else will become easy.

Here you can see relationship between server and client.

If you know what is server and client you already know what is sever-side and client-side, but in the simple term, but in the simple term:

Server-side refers to operations that are performed by the server.

Client-side refers to operations that are performed by the client.

When you encounter any problem when something isn’t synchronizing, ask your self, “Is it server-side or client-side?”

It will help you in most cases.

I would recommend to look up some docs before we start.

RPC

We will use RPC function to send over data or calling functions.

There is two types of RPC:

Reliable: when the function call arrives, an acknowledgement will be sent back; if the acknowledgement isn’t received after a certain amount of time, the function call will be re-transmitted.

Unreliable: the function call is sent only once, without checking to see if it arrived or not, but also without any extra overhead.

Two Approaches To Make Multiplayer

The 1st one is running server which is not running instance of the game and will sync clients.

The 2nd one is similar to first one, but the server actually running instance of the game.

There isn’t very big difference between them, only when you want to do the first approach you will need to create 2nd project.

We will be using the 2nd approach.

World (Map)

First we will start with creating a world node and also we need add some Position2D, it will help us determine where to spawn players (so they wont spawn on themselves), you can also fix that by code.

Our structure should look like this:

I added node named “Spawning_nodes” for clarity

This will be our holder for other objects (enemies, players, etc…)

Menu and Server

We will create new scene (user interface will be “Root Node”) where we need input field(LineEdit) for IP address and button for joining and 2nd button for creating server.

Scene Structure:

Signals

signals allows object to react to a change in another without them referencing one another.

We want to create server when we click on the CreateServer button and join when JoinServer button.

Pretty simple huh?

First we attach script to Network_setup, then we will attach signal to the script from buttons.

We will click to the button switch to Node and we will connect signal pressed() and connect it to Network_setup node which will add function _on_ButtonName_pressed().

Create a server and Join server

Okay now we got Menu and now we just need server, first we need to create global Script, create a script and go to Project Settings -> Autoload and select path to the script and click add (you can name it as you want, but keep in mind that I’ve named it “Network”).

Network.gd

This is whole script for server, there you can see bunch of functions, but we are interested in join_server() and create_server(), these functions using the High-level multiplayer functions which creates server for us.

In the join_server() function is also timer which we are setting in _ready() function which is just taking care of connection timeout.

Now we will update Menu script and just call join_server() or create_server() function according to what was pressed.

Menu.gd

And also we connect signals when _connected_to_server, etc… it will be important later on (now you can try running two instances, if you can connect it should response to output with player id).

Player and Camera

I will use only “icon.png”, but you can download some assets and animate them it’s up to you.

We will create next scene with KinematicBody2D as parent, CollisionShape2D, Sprite(texture) and Camera2D as children.

What is kinematic body?

Kinematic body is special type of body that are meant to be user-controlled.

Scene Structure:

click on the Sprite and drag over into texture your texture in the CollisionShape2D you must setup RectangleShape2D.

Should look like this

Movement

Okay lets move into coding, but first go to Project Settings -> Input Map and setup keys to move around.

I will use _process(), for smoother movement.

Single player movement

Nothing to say here except function move_and_slide(velocity) is setting the position and allowing us to move and collision with other objects.

Turning it into multiplayer, we need somehow send over to all players our position and where we are moving, for this we will be using puppet var and setget.

Player.gd

You will need to add timer and connect signal timeout() which will be sending packets through the network, we will be using rpc_unreliable, because we just need to keep sending the packets as fast as possible.

don’t forget to use auto-start timer

You may also notice I added Tween, it will help soften the movement.

When someone connect to you and he start moving he is actually teleporting and you want reduce this feeling by Tween and when lagged completely, it will keep moving in the same direction.

Instancing Player

For this we will create global scene with node which will be our holder for players.

Again create scene with just Node and go to Project Settings -> Autoload and select path and click add (I named it Persistent_nodes).

I would create 2nd global script with function which we can use through Project (I named it “Global”).

And I would put this inside that script:

Inside Global.gd
Inside Menu.gd (Menu is also our lobby for simplicity)

By this function we will instance player, when we connect to server and when we create server, there isn’t any collision with sync so we don’t have to use any special functions.

inside Menu.gd

I actually added also timer so it can load for the player which is connecting to server.

And also now we need to manage when the player disconnects or player object will be still there and no one will be able to move with him.

inside Menu.gd

We just check Persistent_nodes if there is player with that id and just remove him by “queue_free()”.

Final version of Menu.gd:

Menu.gd

I added Control Node so I can hide when we create/join a server.

Camera fix

What’s wrong with camera?

Instancing a player will add he’s own camera and it will focus new camera instead yours.

How to fix it?

Pretty simple, just when instancing new player check if the local system is the master of this node, make camera current (but we will need to wait one frame, because we are setting the master inside instance_player() function little bit later on).

Inside Player.gd

Finally Switch To Game World

Inside Menu.gd

We will create another button for starting game and connect signal from the button to the Menu.gd which will be calling RPC function which will call sync function switch_to_game().

Now we will create World.gd script and attach it to World(Map), here we just loop through our Spawning_nodes update position of the player according the node

World.gd

Should look like this, we need also handle _player_disconnected().

Enemy

Enemy will be similar to Player, but we will need some to add two more timers and two Area2D.

We are going to use Area2D to determine if player inside damage and 2nd Area2D for switching targets if someone will cross our enemy.

Timer will be our attack cool-down.

Here you can go pretty insane, but we are going to stick to the simplicity.

Scene Structure:

setup should look like this
on both timers set “One Shot”: true

Coding

Here will not be anything new, same as player except now we are using _physics_process() and movement is not by keys but from direction_to() function which will return vector which will be pointing to our position.

And we will need to add player into player group, because when enemy is created it will call the function on him self.

Click on the KinematicBody2D -> Node -> Groups.

Keep in mind it’s case sensitive

Also you can notice the loop which will loop through players and if player inside Area2D and timer stopped it will damage our player, but we didn’t declare this function, so let’s repair that.

This will be our health system

Likewise the movement, we are going to use puppet var and setget to keep track of health and when we reach 0 health, we call sync function which will free our player.

Now everything should work just fine, but when enemy is created it don’t know who to target.

We can fix it by keeping track of alive players in global script or we can just loop through Persistent_nodes and player select from there (you can make it random).

Inside Enemy.gd

BONUS

Random Number Generator

what is the problem here?

Well if you create it only on client-side it will generate completely different numbers so be careful, it could introduce some issues.

You will need to use “seed()” function which sets the generation

(you can know this approach from “Minecraft”)

We just generate random integer and send it into sync function which will apply that seed and now we can generate random numbers, because we are using the same seed on the client-side and server-side.

Enemy Spawner

what is the problem here?

When you are instancing enemy in multiplayer, you need to sync everything also the name.

If you don’t, simply they won’t sync.

Conclusion

We created game which provides movement, enemies and it’s all sync with multiple players, from this point you can create GUI, story, etc…

If you still didn’t notice yet, just create single player and on it add some functions which send over data and you got multiplayer.

It’s also good to mention that sorting the folder structure brings some clarity in your project, but I created just small project so it’s no that big need for me.

Thank you for reading and hopefully see you in the next article.

--

--

Sw33tBit

Programmer who is passionate about code and making things work. I'm always looking to learn new technologies.