Using the new Unity input system with MLAPI in a multiplayer game (Part 2)

Brandon Lara
Geek Culture
Published in
8 min readAug 19, 2021
Photo by Hello Lightbulb on Unsplash

Hello again, this is the second part (and last) of the guide to implement the players’ movement using the Unity new input system in a multiplayer game, in which the network has been built with MLAPI,

In the first part, we already saw how to set up the entire input system, and now we are going to set up MLAPI, the network objects and implement the network movement logic.

If you have not yet configured and built the entry system or if you have any problems with the entry system, it's highly recommendable to check the first part of this guide at the following link:

In addition, you can check the entire Unity project of this guide (Including the scripts, assets, components, etc. shown in the guide) at the following link:

For more information, below you can see in detail the contents of the two parts of this guide:

Contents of part 1:

  • Install Input System Package.
  • Create the Input Actions.
  • Create the Player Prefab.
  • Create the Player Movement Script.

Contents of part 2:

  • Install MLAPI Package.
  • Create the Network Manager Object.
  • Create the World Manager Object and Script.
  • Implement the Networking Movement.
  • Testing the game

Install the MLAPI Package

To install the MLAPI package, it is necessary to install Git if you do not have it installed on your PC. After the installation, it’s required to restart the PC to make it work. Then we have to open the Package manager from the main menu Window > Package Manger. In the Package manager click on the + button in the status bar and select Add package from git URL, there enter the Git URL to the MLAPI release package below:

https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi.git?path=/com.unity.multiplayer.mlapi#release/0.1.0

After the installation, the package is shown as below:

Create the Network Manager Object

To begin with, we need to create the Network Manager object by right-clicking in the Hierarchy tab of the Main Unity Window and selecting Create Empty. This creates a new GameObject that you can rename it as you want, but it's recommendable to rename it to NetworkManager.

Then select the new GameObject, go to the Inspector Tab, and add the NetworkManager component located in the MLAPI group. This new component needs to select the correct transport, to do this locate the NetworkTransport field, click Select Transport and select UnetTransport.

Next, we have to select the prefab Player and add a NetworkObject component to it.

Create the World Manager Object and Script

In this step, I will create a GameObjectcalled WorldManager and a Script that will be added to it, and in which a simple GUI will be implemented consisting of buttons to start the game as a host, as a client or as a server. For this reason, this step is not mandatory, but it is highly recommended.

Similarly to what we did before, we have to select Create Empty in the Hierarchy tab of the Main Unity Window, and then rename the new GameObject to WorldManager. Also, we have to create a new Script in the Asset folder or Script folder, if we created one inside the Asset folder. We can rename this Script as we want, but remember that this Script is going to be added as a component to the WorldManager GameObject, so I'm going to name it WorldManager.cs.

In this script, you have to implement the drawing of the buttons and the logic to start as host, client, or server, which is triggered by pressing the corresponding button. To do this I have based on the code from the HelloWorldManager.cs script that can be found in the following link https://docs-multiplayer.unity3d.com/docs/tutorials/helloworld/helloworldtwo

I have made this modification to implement just the needed for the GUI, and you can see it or copy it from the code below:

Implement de Networking Movement

The movement has to be implemented by synchronizing the position of the players on all networked connector clients. To achieve this, we have to modify the player script (In my case Player.cs) where previously was created the OnMovement method for handling the Move action.

In the script, the first thing we have to do is to create a NetworkVariable to represent this player’s networked position. This depends on your implementation of the movement, in my case it's NetworkVariableVector3 as you can see below:

Before continuing, it is important to change the inherited class from MonoBehaviour to NetworkBehaviour, so that we can use the variables of the NetworkObject (IsLocalPlayer, IsServer, IsClient,…) in the implementation of the motion logic.

public class Player : NetworkBehaviour

Next, in the OnMovement method, we have to implement the logic of the movement properties based on the parameter value, but we have to control that this is executed only when the player instance is the local player, this way in all clients only change the movement properties on the local player without affecting the network players.

Below you can see that in my case I only change the movement direction to left or right based on the parameter value, only if the IsLocalPlayer property is true:

Following this, we need to set the Position NetworkVariable based on the movement properties, but note that this can only be done on the server-side. Then we have to set transform position with the networked Position value, this may seem redundant or that the code can be shortened, but all this is necessary for the position synchronization to be correct in all clients.

On the other hand, my movement implementation is constantly executing in the FixedUpdate method as shown below:

As you can see in the code, if the variable IsServer is true, I set the variable Position with the sum of the last value of Position and the current direction. Otherwise, if the IsServer variable is false, I simply send a message to the server via RPC, to set the Position based on the current direction given by the client, to do this you have to implement a ServerRPC method.

This logic means that if the code has been executed in the server instance, we can change the value of the network variable Position without any problems. Otherwise, if we are on the client-side, we must ask the server to make this change, this is done by executing the method labelled with [ServerRPC], since every method that has this label is executed on the server instance, and not on the client one, even though the latter is the one calling the method.

In my example, I implemented the ServerRPC method SetPositionServerRpc, which assigns the position value in the same way as seen before when IsServer is true.

You can see this implementation of the SetPositionServerRpc method below:

Finally, I set the transform position with the Position value to execute the movement on the game, so now we just have to test the game is working properly.

The reason all these network property checks are performed is that independent Player prefabs from all networked instances are spawned on the host/server game instance, and on all client game instances, or in other words, MLAPI spawns a Player prefab for each entity connected. Furthermore, this means that each game instance will run the Player script for each networked entity independently between entities and game instances, and therefore will not share any of its properties either within the same instance or between game instances.

For example, you can see below a diagram for a multiplayer game with a host/server game instance, and two client game instances connected, this diagram shows how all the Player prefabs are spawned in each instance and the value of each networked property:

As we can see, in the Host/Server, Client1, and Client 2 instances MLAPI spawned its own Player prefab and that of each of the connected game instances, these prefabs have the same properties, but are not shared, and may have different values.

The player script executes one instance for each prefab as it has them as components, but the properties and execution are independent between the different Player prefabs, and between the game instances, as can be seen in the diagram, where the network properties vary for each prefab Player. With this behaviour, it's impossible to synchronize the position values, that's why I used the NetworkVariableVector3 because this type of variable is synchronized between the same Player prefabs and scripts across each game instance.

In the diagram, in the Host/Server player script, the position value is the same on the Host/Server, Client1 and Client2 instances, but each player prefab has its own position value that it's synchronized between instances. If you want to share information between prefabs of the same instance, maybe it's a good idea to do it through the World Manager.

Testing the game

To test the multiplayer game, we need to run at least two game instances, the easiest way to do this is by running one standalone instance of the game and another with the Unity editor.
To run a standalone instance of the game, follow these steps:

  1. Open the Build Settings window via File>Build Settings.
  2. In the Build Settings windows switch to the PC, Mac and Linux Standalone platform.
  3. Click on the button Build and Run, or close the window and select File>Build and Run.

If everything went well, at this point the movement of the network game should work and synchronize correctly. The best part of configuring and coding this is that MLAPI and the New Input System are exciting and interesting solutions that are currently a trend in Unity game development.

As I always say, if you have any questions or suggestions, you can comment without any problem 😉, or take a look at the first part of this guide, which can be accessed through the following link:

Last but not least, you can find the entire Unity project of this guide (With scripts, assets, configuration, etc…) at the following link:

I hope that all this has helped you, or motivated you to continue with the development of your games 😃👍

Cheers!

More information on the topics in this guide can be found in the following links:

--

--