In Unity, creating new game objects with Instantiate() is sometimes called “spawning”. In the network HLAPI the word “spawn” is used to mean something more specific. In the server authoritative model of the network HLAPI, to “spawn” an object on the server means that the object should be created on clients connected to the server, and the object will be managed by the spawning system. Once in the spawning system, state updates are sent to clients when the object changes on the server, and the object will be destroyed on clients when it is destroyed on the server. Spawned objects are also added to the set of networked objects that the server is managing, so that if another client joins the game later, the objects will also be spawned on that client. These objects have a unique network instance Id called “netId” that is the same on the server and clients for each object. This is used to route messages to objects and to identify objects.
When NetworkIdentity objects are spawned on clients, they are created with the current state of the object on the server. This applies to the object’s transform, movement state and synchronized variables. So client objects will always be up-to-date when they are created. This avoids issues such as objects being spawned at a wrong initial location, then popping to their correct position when a state update packet arrives.
This sounds great, but there are some immediate questions that this brings up. How is the object created on the clients? And, what if the objects changes in between the time when it is spawned and another client connects? Which version of the object is spawned for the new client then?
Spawning of objects on clients instantiates the client object from the prefab of the object that was passed to NetworkServer.Spawn on the server. The NetworkIdentity inspector preview panel shows the Asset ID of the NetworkIdentity, it is this value that identifies the prefab so that the client can create the objects. For this to work efficiently, there is a registration step that clients have to perform; they must call ClientScene.RegisterPrefab to tell the system about the asset that the client object will be created from.
Registration of spawn prefabs is most conveniently done by the NetworkManager in the editor. The “Spawn Info” section of the NetworkManager allows you to register prefabs without writing any code. This can also be done through code when a NetworkClient is created. To do it in code:
using UnityEngine;
using UnityEngine.Networking;
public class MyNetworkManager : MonoBehaviour
{
public GameObject alienPrefab;
NetworkClient myClient;
// Create a client and connect to the server port
public void SetupClient()
{
ClientScene.RegisterPrefab(alienPrefab);
myClient = new NetworkClient();
myClient.RegisterHandler(MsgType.Connect, OnConnected);
myClient.Connect("127.0.0.1", 4444);
}
}
In this example, the user would drag a prefab asset onto the alienPrefab slot on the MyNetworkManager script. So that when a alien object is spawned on the server, the same kind of object will be created on the clients. This registration of assets ensures that the asset is loaded with the scene, so that there is no stall to load the asset when it is created. For more advanced use cases such as object pools or dynamically created assets, there is ClientScene.RegisterSpawnHandler which allows callback functions to be registered for client side spawning.
Below is a simple example of a spawner that creates a tree with a random number of leaves.
class Tree : NetworkBehaviour
{
[SyncVar]
public int numLeaves;
}
class MySpawner : NetworkBehaviour
{
public GameObject treePrefab;
public void Spawn()
{
GameObject tree = (GameObject)Instantiate(treePrefab, transform.position, transform.rotation);
tree.GetComponent<Tree>().numLeaves = Random.Range(10,200);
NetworkServer.Spawn(tree);
}
}
To complete this example, the project would have a prefab asset for the tree that has the Tree script and a NetworkIdentity component. Then on the MySpawner instance in the scene, the treePrefab slot would be populated by the tree prefab asset. Also, the tree prefab must be registered as a spawnable object - either using the NetworkManager UI, or using ClientScene.RegisterPrefab() in code.
When this code runs, the tree objects created on clients will have the correct value for numLeaves from the server.
The actual flow of operations that takes place for spawning is:
Player objects in the network HLAPI are special in some ways. The flow for spawning player objects with the NetworkManager is:
Note that OnStartLocalPlayer() is called after OnStartClient(), as it only happens when the ownership messages arrives from the server after the player object is spawned. So isLocalPlayer will not be set in OnStartClient().
Since OnStartLocalPlayer is only called for YOUR player, it is a good place to perform initialization that should only be done for the local player. This could include enabling input processing, and enabling camera tracking for the player object. Typically only the local player has an active camera.
The default behaviour of creating spawned objects from prefabs on the client can be customized by using spawn handler functions. You can register functions to spawn and un-spawn client objects with ClientScene.RegisterSpawnHandler. This function takes two function delegates, one to handle creating objects on the client and one to handler destroying objects on the client.
// Handles requests to spawn objects on the client
public delegate GameObject SpawnDelegate(Vector3 position, NetworkHash128 assetId);
// Handles requests to unspawn objects on the client
public delegate void UnSpawnDelegate(GameObject spawned);
The assetId passed to the spawn function can be found on NetworkIdentity.asset for prefabs, where it is populated automatically. You can spawn objects with custom assetIds by using code like:
// generate a new unique assetId
NetworkHash128 creatureAssetId = new NetworkHash.Parse("e2656f");
// register handlers for assetId
ClientScene.RegisterSpawnHandler(creatureAssetId, SpawnCreature, UnSpawnCreature);
// spawn a creature - SpawnCreature will be called on client.
NetworkServer.Spawn(gameObject, creatureAssetId);
Note that on the host, object are not spawned for the local client as they already exist on the server. So no spawn handler functions will be called.