You can use spawn handler functions to customize the default behavior when creating spawned game objects on the client. Spawn handler functions ensure you have full control of how you spawn the game object, as well as how you destroy it.
Use NetworkClient.RegisterSpawnHandler or NetworkClient.RegisterPrefab to register functions to spawn and destroy client game objects. The server creates game objects directly, and then spawns them on the clients through this functionality. This functions takes either the asset ID or a prefab and two function delegates: one to handle creating game objects on the client, and one to handle destroying game objects on the client. The asset ID can be a dynamic one, or just the asset ID found on the prefab game object you want to spawn.
The Spawn / Unspawn delegates will look something like this:
Spawn Handler
GameObjectSpawnDelegate(Vector3 position,uint assetId) { // do stuff here}
or
GameObjectSpawnDelegate(SpawnMessage msg) { // do stuff here}
UnSpawn Handler
voidUnSpawnDelegate(GameObject spawned) { // do stuff here}
When a prefab is saved its assetId field will be automatically set. If you want to create prefabs at runtime you will have to generate a new GUID.
Generate Prefab At Runtime
// generate a new unique assetId System.Guid creatureAssetGuid =System.Guid.NewGuid();uint creatureAssetId =NetworkIdentity.AssetGuidToUint(creatureAssetGuid);// register handlers for the new assetIdNetworkClient.RegisterSpawnHandler(creatureAssetId, SpawnCreature, UnSpawnCreature);
Use existing prefab
// register prefab you'd like to custom spawn and pass in handlersNetworkClient.RegisterPrefab(coinAssetId, SpawnCoin, UnSpawnCoin);
Spawn on Server
// spawn a coin - SpawnCoin is called on clientNetworkServer.Spawn(gameObject, coinAssetId);
The spawn functions themselves are implemented with the delegate signature. Here is the coin spawner. The SpawnCreature would look the same, but have different spawn logic:
When using custom spawn functions, it is sometimes useful to be able to unspawn game objects without destroying them. This can be done by calling NetworkServer.UnSpawn. This causes the object to be Reset on the server and sends a ObjectDestroyMessage to clients. The ObjectDestroyMessage will cause the custom unspawn function to be called on the clients. If there is no unspawn function the object will instead be Destroy
Note that on the host, game objects are not spawned for the local client, because they already exist on the server. This also means that no spawn or unspawn handler functions are called.
Pooling Game Objects
To avoid Instantiating & Destroying heavily used GameObjects, it can be useful to pool them instead.
Here is an example of how you might set up a simple game object pooling system with custom spawn handlers. Spawning and unspawning then puts game objects in or out of the pool.
usingUnityEngine;namespaceMirror.Examples{publicclassPrefabPool:MonoBehaviour { // singleton for easier access from other scriptspublicstaticPrefabPool singleton; [Header("Settings")]publicGameObject prefab; [Header("Debug")]publicint currentCount;publicPool<GameObject> pool;voidStart() {InitializePool(); singleton =this;NetworkClient.RegisterPrefab(prefab, SpawnHandler, UnspawnHandler); } // used by NetworkClient.RegisterPrefabGameObjectSpawnHandler(SpawnMessage msg) =>Get(msg.position,msg.rotation); // used by NetworkClient.RegisterPrefabvoidUnspawnHandler(GameObject spawned) =>Return(spawned);voidOnDestroy() {NetworkClient.UnregisterPrefab(prefab); }voidInitializePool() { // create pool with generator function pool =newPool<GameObject>(CreateNew,5); }GameObjectCreateNew() { // use this object as parent so that objects dont crowd hierarchyGameObject next =Instantiate(prefab, transform);next.name=$"{prefab.name}_pooled_{currentCount}";next.SetActive(false); currentCount++;return next; } // Used to take Object from Pool. // Should be used on server to get the next Object // Used on client by NetworkClient to spawn objectspublicGameObjectGet(Vector3 position,Quaternion rotation) {GameObject next =pool.Get(); // set position/rotation and set activenext.transform.position= position;next.transform.rotation= rotation;next.SetActive(true);return next; } // Used to put object back into pool so they can b // Should be used on server after unspawning an object // Used on client by NetworkClient to unspawn objectspublicvoidReturn(GameObject spawned) { // disable objectspawned.SetActive(false); // add back to poolpool.Return(spawned); } }}
To use this pool, add the PrefabPool component (code above) to the NetworkManager. Next, drag a prefab you want to spawn multiple times to the Prefab field.
Make sure to remove the prefab from NetworkManager's spawnable prefabs list. There should only be one way to spawn it. Otherwise Mirror show a warning.
We can modify our Tanks example to showcase the Pooling system.
Instead, we simply Unspawn it and return it to the pool:
[Server]voidDestroySelf(){ // return to prefab poolNetworkServer.UnSpawn(gameObject);PrefabPool.singleton.Return(gameObject);}
Press Play and fire some projectiles. Notice how nothing is instantiated. Instead, NetworkManager has a pool of children which are disabled until they are needed.