Golden Path Module One
In this guide we will be covering the following:
- Adding scripts to your objects
- Adding editor modes inside your game (Host Server and Client)
- Basic Player Movement
- Permissions
- Basic RPC use
Requirements
You should have completed the foundation module here before starting this tutorial..
Create a Clone 'GoldenPath' project
The clone does not support rollback so it will only work on the unity version that it was originaly created for. Attempting to open the clone on a newer or older version of Unity may cause unexpected funcationality.
We create a clone of the Golden Path foundation project that you created, if you haven't yet created a GoldenPath
project then you should follow the steps here to create one.
Working from a clone of the initial project will help you learn about new features and concepts without having to repeat previous work.
Create a clone of GoldenPath
- Navigate to where you Golden Path project is stored. For this guide we used a Mac and our path was
~/dev/mlapi-golden-path/
. On a Windows machine your path may be slightly different. - Create a new folder.
- Rename the new folder
Golden Path_<modulenumber>
.tipFor the purposes of this series I will replace
modulenumber
with the current module number of the series. You don't have to though you may find it makes following the series easier. - Open the
Goldenpath
folder. - Select Copy.
- Select the folder containg your Golden Path project.
- Select Paste.
I moved my new folder to keep my file structure clean its not a required step, but it does look nice.
Add the clone to Unity Hub
- Open Unity Hub.
- Click Add.
- Navigate to where the clone was saved.
- Select
GoldenPath_<modulenumber>
. - Click Open.
The Clone should now appear in the project list of Unity Hub as GoldenPath_<modulenumber>
.
Open GoldenPath_One
- Open Unity Hub.
- Select
GoldenPath_One
from the list of projects displayed.
Adding Scripts to Goldenpath
This section will add some scripts to Goilden Path which will contain the new features we will be covering in this module.
Click the Assets folder.
Create a new Folder and call it Scripts.
Create an empty
GameObject
rename it HelloWorldManager.Create a script called
HelloWorldManager
.Add the
HelloWorldManager
script as a component.Open the
HelloWorldManager.cs
script.Edit the
HelloWorldManager.cs
script to match the following.
You can copy the script from here and paste it into your file.
- Select the code sample.
- Click Copy in the top right corner.
- Paste it into your code editor.
Click to show/hide the Code.
using MLAPI;
using UnityEngine;
namespace HelloWorld
{
public class HelloWorldManager : MonoBehaviour
{
void OnGUI()
{
GUILayout.BeginArea(new Rect(10, 10, 300, 300));
if (!NetworkManager.Singleton.IsClient && !NetworkManager.Singleton.IsServer)
{
StartButtons();
}
else
{
StatusLabels();
SubmitNewPosition();
}
GUILayout.EndArea();
}
static void StartButtons()
{
if (GUILayout.Button("Host")) NetworkManager.Singleton.StartHost();
if (GUILayout.Button("Client")) NetworkManager.Singleton.StartClient();
if (GUILayout.Button("Server")) NetworkManager.Singleton.StartServer();
}
static void StatusLabels()
{
var mode = NetworkManager.Singleton.IsHost ?
"Host" : NetworkManager.Singleton.IsServer ? "Server" : "Client";
GUILayout.Label("Transport: " +
NetworkManager.Singleton.NetworkConfig.NetworkTransport.GetType().Name);
GUILayout.Label("Mode: " + mode);
}
static void SubmitNewPosition()
{
if (GUILayout.Button(NetworkManager.Singleton.IsServer ? "Move" : "Request Position Change"))
{
if (NetworkManager.Singleton.ConnectedClients.TryGetValue(NetworkManager.Singleton.LocalClientId,
out var networkedClient))
{
var player = networkedClient.PlayerObject.GetComponent<HelloWorldPlayer>();
if (player)
{
player.Move();
}
}
}
}
}
}
Adding Editor Modes to Hello World
Inside the HelloWorldManager.cs
script, we define two methods which mimic the editor buttons inside of NetworkManager during Play mode.
Click to show/hide the Code.
static void StartButtons()
{
if (GUILayout.Button("Host")) NetworkManager.Singleton.StartHost();
if (GUILayout.Button("Client")) NetworkManager.Singleton.StartClient();
if (GUILayout.Button("Server")) NetworkManager.Singleton.StartServer();
}
static void StatusLabels()
{
var mode = NetworkManager.Singleton.IsHost ?
"Host" : NetworkManager.Singleton.IsServer ? "Server" : "Client";
GUILayout.Label("Transport: " +
NetworkManager.Singleton.NetworkConfig.NetworkTransport.GetType().Name);
GUILayout.Label("Mode: " + mode);
}
NetworkManager
implements the singleton pattern as it declares its singleton named Singleton
. This is defined when the MonoBehaviour
is enabled. This component also has useful properties, such as IsClient
, IsServer
, and IsLocalClient
. The first two dictate the connection state we have currently established that you will use shortly.
We call these methods inside of OnGUI()
.
Click to show/hide the Code.
void OnGUI()
{
GUILayout.BeginArea(new Rect(10, 10, 300, 300));
if (!NetworkManager.Singleton.IsClient && !NetworkManager.Singleton.IsServer)
{
StartButtons();
}
else
{
StatusLabels();
SubmitNewPosition();
}
GUILayout.EndArea();
}
You won'tice the introduction of a new method, SubmitNewPosition()
; which we will be using later.
Adding basic movement to the Player object
This script adds some basic movement to the Hello World player.
- Create a new script
HelloWorldPlayer
. - Open the
HelloWorldPlayer.cs
script. - Edit the
HelloWorldPlayer.cs
script to match the following.
Click to show/hide the Code.
using MLAPI;
using MLAPI.Messaging;
using MLAPI.NetworkVariable;
using UnityEngine;
namespace HelloWorld
{
public class HelloWorldPlayer : NetworkBehaviour
{
public NetworkVariableVector3 Position = new NetworkVariableVector3(new NetworkVariableSettings
{
WritePermission = NetworkVariablePermission.ServerOnly,
ReadPermission = NetworkVariablePermission.Everyone
});
public override void NetworkStart()
{
Move();
}
public void Move()
{
if (NetworkManager.Singleton.IsServer)
{
var randomPosition = GetRandomPositionOnPlane();
transform.position = randomPosition;
Position.Value = randomPosition;
}
else
{
SubmitPositionRequestServerRpc();
}
}
[ServerRpc]
void SubmitPositionRequestServerRpc(ServerRpcParams rpcParams = default)
{
Position.Value = GetRandomPositionOnPlane();
}
static Vector3 GetRandomPositionOnPlane()
{
return new Vector3(Random.Range(-3f, 3f), 1f, Random.Range(-3f, 3f));
}
void Update()
{
transform.position = Position.Value;
}
}
}
- Select the Player prefab.
- Add the script
HelloWorldPlayer
script as a component.
This class will inherit from NetworkBehaviour
instead of MonoBehaviour
.
Click to show/hide the Code.
public class HelloWorldPlayer : NetworkBehaviour
Inside this class we now define a NetworkVariable
to represent this player's networked position.
Click to show/hide the Code.
public NetworkVariableVector3 Position = new NetworkVariableVector3(new NetworkVariableSettings
{
WritePermission = NetworkVariablePermission.ServerOnly,
ReadPermission = NetworkVariablePermission.Everyone
});
Introducing permissions
In the HelloWorldPlayer.cs
script we introduce read and write permissions on a NetworkVariable
. For the purposes of this demo, the server will be authoritative on the NetworkVariable
representing position. All clients are able to read the value, however.
HelloWorldPlayer
overrides NetworkStart
.
Click to show/hide the Code.
public override void NetworkStart()
{
Move();
}
Any MonoBehaviour
implementing NetworkBehaviour
can override the MLAPI method NetworkStart()
. This method is fired when message handlers are ready to be registered and the networking is setup. We override NetworkStart
since a client and a server will run different logic here.
This can be overriden on any NetworkBehaviour
.
On both client and server instances of this player, we call the Move()
method, which will simply do the following.
Click to show/hide the Code.
public void Move()
{
if (NetworkManager.Singleton.IsServer)
{
var randomPosition = GetRandomPositionOnPlane();
transform.position = randomPosition;
Position.Value = randomPosition;
}
else
{
SubmitPositionRequestServerRpc();
}
}
Some simple RPC use
If this player is a server-owned player, at NetworkStart()
we can immediately move this player, as suggested in the following code.
Click to show/hide the Code.
if (NetworkManager.Singleton.IsServer)
{
var randomPosition = GetRandomPositionOnPlane();
transform.position = randomPosition;
Position.Value = randomPosition;
}
If we are a client, we call a server RPC.
Click to show/hide the Code.
else
{
SubmitPositionRequestServerRpc();
}
This server RPC simply sets the position NetworkVariable
on the server's instance of this player by just picking a random point on the plane.
Click to show/hide the Code.
[ServerRpc]
void SubmitPositionRequestServerRpc(ServerRpcParams rpcParams = default)
{
Position.Value = GetRandomPositionOnPlane();
}
The server instance of this player has just modified the Position NetworkVariable, meaning that if we are a client, we need to apply this position locally inside of our Update loop.
Click to show/hide the Code.
void Update()
{
transform.position = Position.Value;
}
We can now go back to HelloWorldManager.cs
and define the contents of SubmitNewPosition()
.
Click to show/hide the Code.
static void SubmitNewPosition()
{
if (GUILayout.Button(NetworkManager.Singleton.IsServer ? "Move" : "Request Position Change"))
{
if (NetworkManager.Singleton.ConnectedClients.TryGetValue(NetworkManager.Singleton.LocalClientId,
out var networkedClient))
{
var player = networkedClient.PlayerObject.GetComponent<HelloWorldPlayer>();
if (player)
{
player.Move();
}
}
}
}
Whenever you press the GUI button (which is contextual depending on if you are server or a client), you find your local player and simply call Move()
.
You can now create a build which will show the concepts outlined above.
Make sure SampleScene is included in BuildSettings.
One build instance can create a host. Another client can join the host's game. Both are able to press a GUI button to move. Server will move immediately and be replicated on client. Client can request a new position, which will instruct the server to modify that server instance's position NetworkVariable
. That client will apply that NetworkVariable
position inside of it's Update() method.
Congratulations you have learned the basics of a networked game
Next Steps
For more information on the relevant concepts introduced here please refer to the following sections of the documentation:
This guide would not have been possible without the hard work and support of Fernando Cortez, Unity.