Getting Started with Multiplayer in Unity: Player Movement

Learn how to get started with the newly released Netcode for Game Objects using Unity in just a few steps! Practical explanation + code included.

Seemanta Debdas
Eincode

--

Have you ever wished to make multiplayer games like Fall Guys and Among Us(also made with Unity) but stopped because of how convoluted the process looks?

Well, Unity has officially released Netcode for game objects which lets ambitious developers not remain restricted by the fear of coding Networking logic. With Netcode, you can focus on building your game instead of low-level protocols and networking frameworks.

This makes it easier for Game Devs to focus on the gameplay perspective.

What we will be making

In this part, we will look at how to:

  • Set up our project for multiplayer.
  • Make a simple UI to start the game as a Host, Server, or Client.
  • Synchronizing movement

Throughout the article, I've tried to explain, to the best of my understanding, some basic terminologies related to multiplayer and some which are native to Netcode.

Final Result

Resources

Game-dev Course:

GitHub Repository:

Important Links:

Scene Setup

As stated on the official Netcode documentation:

Netcode supports version 2020.3 and up.

Netcode does not support the WebGL platform because it does not allow access to IP Sockets. It's also stated that by using Netcode, we can create games for any platform other than WebGL. For this, you might need to use third-party transforms.

With that said, let's install Netcode in a new project. You can follow the official Netcode documentation or follow along.

To install Netcode:

  • From the menu bar, select Windows → Package Manager.
  • Click the plus sign(+) and select Add package by name.
  • Copy and paste the following:

com.unity.netcode.gameobjects

  • Click Add.

The first thing we need to add to our scene is a Network Manager.

According to the official docs:

The NetworkManager is a required Netcode for GameObjects (Netcode) component that contains all of your project’s netcode-related settings. Think of it as the “central netcode hub” for your netcode-enabled project.

To add this to our scene,

  • Create an empty Game Object
  • In the Inspector, click on Add Component → Network Manager.

Your hierarchy should resemble something like this:

Network Manager

After that, select a Transport Layer in the Network Manager component.

A transport layer establishes communication between your application and different hosts in a network.

Go ahead and select the latest Unity Transport. That will automatically add the Unity Transport component to the Network Manager.

That's all we needed to set up our scene!

Now that we have multiplayer up and running, let's make the Player prefab that'll get spawned when any client joins.

Player Prefab

Netcode can spawn a default player object for you. If a valid Player Prefab is assigned in the Network, then Netcode will spawn a unique instance of the designated player prefab for each connected and approved Client.

To keep it simple, we'll make our Player a capsule. Go ahead and add a capsule to the scene. Name it "Player". Also, add a plane to the scene.

The scene should resemble this.

Make a "Prefabs" folder and drag and drop the Player game object from the scene to that folder to make it a prefab. You can delete the Player game object from the scene now.

In the Network Manager component, there's a field for Player Prefab.

Network Manager

Drag and drop the prefab you just created into this field. The Network Manager will spawn the assigned Player prefab whenever a new client connects.

But this will throw an error:

This is because, to replicate any Netcode aware properties, i.e., Netcode-related components like NetworkTransform or a NetworkBehaviour with one or more Network Variables or RPCs, a game object must have a NetworkObject component.

So, go ahead and add the NetworkObject component to the Player prefab.

NetworkTransform, NetworkBehaviour, NetworkVariables, and RPCs will be covered soon.

Now that we have a player prefab, it's time to add some logic to move our Player.

Player Controls

Let's first create a "Scripts" folder. Open it up and create a script called "PlayerControls".

I used simple logic for this project to manipulate the Player's movement.

Here, we get the input on the horizontal and vertical axes, then use them to create a normalized "moveDirection" vector. The vector, multiplied by speed, is fed into the Translate function.

That's all that's needed to be done in the PlayerControl script.

Make sure to add this to the Player prefab.

Now, let's create some UI Buttons to give the player options to start the game as a Host, Server, or just as a Client. We'll also be writing a simple script to handle UI Interactions.

UI Manager

Start Host, Server, and Client buttons in Network Manager

Looking at the bottom of the NetworkManager, we'll find 3 Buttons. One for Starting the game as a Host, one as a Server, and the last one as a client.

Now, what do these terms mean?

Server

A server is the authoritative source of events in a multiplayer video game.

In general, servers:

  • Oversee handshaking and game setup for the clients.
  • Receive client requests to perform a specific action or call an event.
  • Send game state data to all clients.
  • It Runs NPC AI.
  • It handles graphics, computation, and physics depending on the game type.

Clients send their input, states, and data to the Server, and the Server syncs it with all the other clients. This way, all the clients know what the other clients are doing. Or, in a sense, what the state of every other Client is, i.e., are they walking, sitting, jumping, and so forth?

Server-Client relationship

Client

Clients connect individual users to the Server.

In general, clients:

  • Are responsible for receiving input from the user (from the local machine).
  • Send movement/action requests to the Server.
  • Update the game state and the state of other clients based on data passed back from the Server.

Host

Hosts are setups that act as both the Server as well as the Client. This means one of the players will both play and own the game world, and other players will connect to it. This has performance, security, and cost considerations.

Network Manager has Editor Buttons to start the game as any of those mentioned above. Let's make a simple in-game UI and hook it up with those buttons.

Set up the Main Canvas:

  • Make a new Canvas → Call it "Main Canvas."
  • Make three buttons as a child of the Main Canvas → One for starting as a host, one as a server, and one as a client.
  • Add a text(Game object having Text Component) as a child of the Main Canvas. (I've used TextMeshPro).
Scene after setting up UI

Once that's completed, add a script. Call it "UIManager".

To start a server, we use a function that can be accessed from the NetworkManager Singleton:

NetworkManager.Singleton.StartServer());

This returns a boolean denoting whether or not the Server was successfully started.

Similarly, for starting host and Client, we use:

NetworkManager.Singleton.StartHost();
NetworkManager.Singleton.StartClient();

Back in Visual Studio,

  • Add a serialized field for the Text we just created.
  • Make three public methods, the same as the code snippet shown above. Each of them will be hooked to the OnClick event of the buttons we just made.

Back in Unity, attach the UIManager to the Canvas containing the buttons.

Drag and drop the Game Object having the Text component into the Text field of the UIManager.

On each of the OnClick events of the buttons:

  • Click on the plus(+) icon to add to the list.
  • Drag and drop the Main Canvas game object to the Object field.
  • In place of No Function, select UIManager StartHost/StartClient/StartServer according to the respective button.

We can choose between starting the game as a server, host, or Client.

Now, before testing this out, we'll be importing a handy package that'll save a lot of time for testing.

Parallel Sync

If not for Parallel Sync, we would have to build every time we made a slight change in our project and wanted to test it out.

Parallel Sync allows us to test multiplayer gameplay without building the project. It creates a clone of your project and an editor for each clone of the project. The editors reflect on the changes made to the main project.

To install Parallel Sync, go to:

This will add a Menu called "Parallel Sync."

Under that, select Clones Manager. Once the Editor Window opens up, hit Create new clone button.

After the clone gets created, the Editor window should look something like this:

Click on Open in New Editor to open up the newly created clone.

Finally, we're ready to test!

On the main editor, press Play and click on Start Host. This will spawn the Player prefab, and you can use your keys to move around.

In the second editor, press Play and click on Start Client.

The first thing you'll notice is the status text.

In the host, it'll appear as:

Host Editor View

Whereas, in the Client, it'll appear as:

Client Editor View

Now, if we try moving our character in each editor, it'll appear that just one Player prefab is there for each editor window.

But actually, there are two of them. Looking at the Inspector, you'll see that two "Player(clone)" have been spawned.

Here are the issues:

  • The "PlayerControls" script controls both the Players in their respective editors.
  • The position of the Player in one editor is not synced in another.

Let's handle one issue at a time.

For allowing only the owner of the Network Object to move the Player, let's make some changes to PlayerControl.cs.

First, let's change the class PlayerControl inheriting from NetworkBehaviour instead of Monobehaviour. This will also add the following header → using Unity.Netcode;

public class PlayerControl: NetworkBehaviour

This class is an extension of Monobehaviour with extra functionalities for handling multiplayer.

Next, let's check whether or not we're the owner of the NetworkObject before running the movement script.

The NetworkObject component provides information about the Players, one of which is the IsOwner property.

Let's make that modification in the previous code like this:

This will prevent us from moving NetworkObjects whose IsOwner property is not set to true.

This solves the movement issue, but we'll still be unable to sync movement. This is where NetworkTransforms come in.

NetworkTransform & ClientNetworkTransform

The position, rotation, and scale of a NetworkTransform are typically only synchronized once when that object is spawned. To synchronize position, rotation, and scale in real-time during the game, a NetworkTransform component is. NetworkTransform synchronizes the transform from the server object to the clients.

But here's the thing,
NetworkTransform synchronizes position, rotation, and scale from the Server to the clients. It does not allow any position, rotation, and scale changes from the Client's side.

Now, what does this mean? Let's add the NetworkTransform component to the Player prefab.

And press Play on both editors. On one editor, select Host, and on another, select Client.

Notice that, under Syncing, all the values that don’t need syncing are turned off. For example, our Player doesn’t jump, rotate or scale up and down. So, all of those values don’t need syncing. We do not want to be syncing any unnecessary data.

Upon playing, you might face the same issue as the video above.

The problem is that,
By default, Netcode is Server authoritative, which means that only the Server has the ownership to change the current game state. Whereas, when the Client tries to move, the Server doesn't allow it since the Client doesn't have ownership. That is why the Host(Server + Client) data is getting synchronized.

We want the owner client(owner of the local Player) to have the ownership to move and tell the Server where its position is supposed to be so that the server syncs that to all the other clients.

[Client ownership is not the best way to go for competitive games]

For this purpose, a ClientNetworkTransform component is more suited.

This script derives from the NetworkTransform script, except it overrides the OnIsServerAuthoritative function to give owner-client authority.

Unfortunately, this doesn't come by default with Netcode. But you can add it by going to:

Once imported, add the ClientNetworkTransform component to the Player prefab.

Similar to NetworkTransform, we'll untick the boxes which don't need to be synced.

Result:

Press Play and see both Players move independently with their positions synchronized.

With that, we sum up this part of the article!

Conclusion

MORE TO COME!

In the next part, we’re going to look at how to send data over the network more efficiently. Syncing movement and rotation with more control(through code). Finally, syncing animations!

If this article piqued your curiosity, you should consider opting for The Complete Unity Guide 3D- Beginner to RPG Game Dev in C# offered by Eincode. This course features among the most immersive and practical resources out there.

Curated by experienced software engineer and freelance developer Filip Jerga, this course starts with the fundamentals. Then, it progresses gradually to eventually take its subscribers through the journey of developing their own RPG game by using Unity 2020 and C#.

--

--

Seemanta Debdas
Eincode
Writer for

Game Dev enthusiast contributing to the Gaming Industry!