The NetworkManager is a component for managing the network state of a multiplayer game. It is actually implemented entirely using the HLAPI, so everything it does is available to developers in other forms. However the NetworkManager wraps up a lot of useful functionality into a single place and makes creating, running and debugging multiplayer games as simple as possible.
The NetworkManager can be used entirely without scripting. It has inspector controls in the editor that allow configuration of all of its features. The NetworkManagerHUD supplies a simple, default user interface at runtime that allows the network game to be controlled by the user. For advanced uses, developers can derive a class from NetworkManager and customize its behaviour by overriding any of the virtual function hooks that it provides.
The NetworkManager features include:
The NetworkManager can be used as the core controlling component of a multiplayer game. To get started, create an empty game object in your starting scene, or pick a convenient manager object. Then add the NetworkManager component from the Network/NetworkManager menu item. The newly added NetworkManager component should look something like:
The inspector for the NetworkManager in the editor allows you to configure and control many things related to networking.
The NetworkManagerHUD is another component that works with the NetworkManager. It gives you a simple user interface when the game is running to control the network state. This is good for getting started with a network project, but not intended to be used as the final UI for a game. The NetworkManagerHUD looks like:
Real games will have a proper user interface for controlling the game state and to allow players to choose what kind of game to play. But, to get started, we can use this to control the game.
A Networking multiplayer game can run in three modes - as a client, as a dedicated server, or as a “Host” which is both a client and a server at the same time. Networking is designed to make the same game code and assets work in all of these cases. Developing for the single player version of the game and the multiplayer version of the game should be the same thing.
NetworkManager has methods for entering each of these modes. NetworkManager.StartClient(), NetworkManager.StartServer() and NetworkManager.StartHost() are all available to script code, so they can be invoked from keyboard input handlers or from custom user interfaces. The default runtime controls that can optionally be displayed also invoke these same functions. There are also buttons in the NetworkManagerHUD inspector available when in play mode that call the same functions:
Whatever method is used to change the game state, the properties networkAddress and networkPort are used. When a server or host is started, networkPort becomes the listen port. When a client is started, networkAddress is the address to connect to, and networkPort is the port to connect to.
The NetworkManager can be used to manage spawning of networked objects from prefabs. Most games have a prefab used as the main player object, so the NetworkManager has a slot to drag the player prefab. When a player prefab is set, a player object will automatically be spawned from that prefab for each user in the game. This applies to the local player on a hosted server, and remote players on remote clients. Note that the player prefab must have the NetworkIdentity component on it.
In addition to the player prefab, the prefabs of other objects that will be dynamically spawned must be registered with the ClientScene. This can be done with the ClientScene.RegisterPrefab() functions, or it can be done by the NetworkManager automatically. Adding prefabs to the spawn list will make them be auto-registered. The spawn configuration section of the NetworkManager inspector looks like:
Once a player prefab is set, you should be able to start the game as a host and see the player object be spawned. Stopping the game should make the player object be destroyed. Running another copy of the game and connecting as a client to localhost should make another player object appear, and stopping that client should make that client’s player object be destroyed.
The player object is spawned by the default implementation of NetworkManager.OnServerAddPlayer. If you want to customize the way player objects are created, you can override that virtual function. The default implementation is something like:
public virtual void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
var player = (GameObject)GameObject.Instantiate(playerPrefab, playerSpawnPos, Quaternion.identity);
NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
}
Note that the function NetworkServer.AddPlayerForConnection() must be called for the newly created player object so that it is spawned and associated with the client’s connection. This will Spawn the object, so NetworkServer.Spawn does not need to be called for the player object.
To control where players are spawned, you can use the NetworkStartPosition component. The NetworkManager looks for NetworkStartPosition objects in a scene, and if it finds any, then it will spawn player at the position and orientation of one of them. Custom code can access the available NetworkStartPositions by the list NetworkManager.startPositions, and there is also a helper function GetStartPosition() on the NetworkManager that can be used in implementation of OnServerAddPlayer to find a start position.
To use start positions, attach a NetworkStartPosition component to an object in the play scene. There can be multiple start positions within a scene. The NetworkManager will then register the position and orientation of the object as a start position. When a client joins the game and a player is added, the player object will be created at one of the start positions, with the same position and orientation.
The NetworkManager has a property PlayerSpawnMethod
which allows configuration of how startPositions
are chosen.
startPosition
options.startPosition
options in a set list.The code for the spawn area looks like this:
if (m_PlayerSpawnMethod == PlayerSpawnMethod.Random && s_StartPositions.Count > 0)
{
// try to spawn at a random start location
int index = Random.Range(0, s_StartPositions.Count);
return s_StartPositions[index];
}
if (m_PlayerSpawnMethod == PlayerSpawnMethod.RoundRobin && s_StartPositions.Count > 0)
{
if (s_StartPositionIndex >= s_StartPositions.Count)
{
s_StartPositionIndex = 0;
}
Transform startPos = s_StartPositions[s_StartPositionIndex];
s_StartPositionIndex += 1;
return startPos;
}
Most games have more than one scene. At very least there is usually a title screen or starting menu scene in addition to the scene where the game is actually played. The NetworkManager is setup to automatically manage scene state and scene transitions in a way that works for a multiplayer game. There are two slots on the NetworkManager inspector, the offlineScene and the onlineScene. Dragging scene objects into these slots activates networked scene management.
When a server or host is started, the online scene will be loaded. This will then become the current network scene. Any clients that connect to that server will be instructed to also load that scene. The name of this scene is stored in the networkSceneName property.
When the network is stopped, by stopping the server or host, or by a client disconnecting, the offline scene will be loaded. This allows the game to automatically return to a menu scene when disconnected from a multiplayer game.
You can also change scenes while the game is active by calling NetworkManager.ServerChangeScene(). This will make all the currently connected clients change scene too, and will update networkSceneName so that new clients will also load the new scene.
While networked scene management is active, any calls to game state managerment functions such NetworkManager.StartHost() or NetworkManager.StopClient() can cause scene changes. This applies to the runtime control UI. So by setting up scenes and calling these functions it is easy to control the flow of a multiplayer game.
Note that scene changes cause all the objects in the scene to be destroyed. This could include the NetworkManager! If you want the NetworkManager to persist between scenes, then ensure the “Dont Destroy On Load” checked boxed is set to true. In simple cases, this is the best configuration. But, it is possible to have a NetworkManager in each scene with different settings to control incremental prefab loading, or different scene transitions.
The NetworkManagerHUD inspector panel shows additional information about the state of the network at runtime. This includes:
Also, registered client message handlers are shown in the preview window.
The NetworkManager runtime UI and NetworkManager inspector UI allow interactions with the matchmaker service. The function NetworkManager.StartMatchmaker() enables matchmaking, and populates the NetworkManager.matchmaker property with a NetworkMatch object. Once this is active, the default UIs use it and callback functions on NetworkManager to let you perform simple matchmaking.
There are virtual functions on NetworkManager that derived classes can use to customize behaviour of responding to matchmaker callbacks.
There are virtual functions on NetworkManager that derived classes can use to customize behaviour. When implementing these functions, be sure to take care of the functionality that the default implementations provide. For example, in OnServerAddPlayer(), the function NetworkServer.AddPlayer must be called to active the player object for the connection.
Functions invoked on the Server/Host:
// called when a client connects
public virtual void OnServerConnect(NetworkConnection conn);
// called when a client disconnects
public virtual void OnServerDisconnect(NetworkConnection conn)
{
NetworkServer.DestroyPlayersForConnection(conn);
}
// called when a client is ready
public virtual void OnServerReady(NetworkConnection conn)
{
NetworkServer.SetClientReady(conn);
}
// called when a new player is added for a client
public virtual void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
var player = (GameObject)GameObject.Instantiate(playerPrefab, playerSpawnPos, Quaternion.identity);
NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
}
// called when a player is removed for a client
public virtual void OnServerRemovePlayer(NetworkConnection conn, short playerControllerId)
{
PlayerController player;
if (conn.GetPlayer(playerControllerId, out player))
{
if (player.NetworkIdentity != null && player.NetworkIdentity.gameObject != null)
NetworkServer.Destroy(player.NetworkIdentity.gameObject);
}
}
// called when a network error occurs
public virtual void OnServerError(NetworkConnection conn, int errorCode);
Functions invoked on the Client:
// called when connected to a server
public virtual void OnClientConnect(NetworkConnection conn)
{
ClientScene.Ready(conn);
ClientScene.AddPlayer(0);
}
// called when disconnected from a server
public virtual void OnClientDisconnect(NetworkConnection conn)
{
StopClient();
}
// called when a network error occurs
public virtual void OnClientError(NetworkConnection conn, int errorCode);
// called when told to be not-ready by a server
public virtual void OnClientNotReady(NetworkConnection conn);
Functions invoked for the Matchmaker:
// called when a match is created
public virtual void OnMatchCreate(CreateMatchResponse matchInfo)
// called when a list of matches is received
public virtual void OnMatchList(ListMatchResponse matchList)
// called when a match is joined
public void OnMatchJoined(JoinMatchResponse matchInfo)