|
| 1 | +# Network prefab handler |
| 2 | + |
| 3 | +The network prefab handler system provides advanced control over how network prefabs are instantiated and destroyed during runtime. This allows overriding the default Netcode for GameObjects [object spawning](../basics/object-spawning.md) behavior by implementing custom prefab handlers. |
| 4 | + |
| 5 | +The network prefab handler system is accessible from the [NetworkManager](../components/networkmanager.md) as `NetworkManager.PrefabHandler`. |
| 6 | + |
| 7 | +## Overview |
| 8 | + |
| 9 | +For an overview of the default object spawning behavior, see the [object spawning](../basics/object-spawning.md) page. The default spawning behavior should cover the majority of spawning use cases, however there are scenarios where you may need more control: |
| 10 | + |
| 11 | +- **Object pooling**: Reusing objects to reduce memory allocation and initialization costs. |
| 12 | +- **Performance optimization**: Using different prefab variants on different platforms (e.g. using a simpler object for server simulation). |
| 13 | +- **Custom initialization**: Setting up objects with game client specific data or configurations. |
| 14 | +- **Conditional spawning**: Initializing different prefab variants based on runtime conditions. |
| 15 | + |
| 16 | +The prefab handler system addresses these needs through a interface-based architecture. The system relies on two key methods, `Instantiate` and `Destroy`. `Instantiate` is called on non-authority clients when an [authority](../terms-concepts/authority.md) spawns a new [NetworkObject](../basics/networkobject.md) that has a registered network prefab handler. `Destroy` is called on all game clients whenever a registered [NetworkObject](../basics/networkobject.md) is destroyed. |
| 17 | + |
| 18 | +## Creating a prefab handler |
| 19 | + |
| 20 | +Prefab handlers are classes which implement on of the Netcode for GameObjects prefab handler descriptions. There are currently two such descriptions: |
| 21 | + |
| 22 | +- **INetworkPrefabInstanceHandler**: This is the simplest interface for custom prefab handlers. |
| 23 | +- **NetworkPrefabInstanceHandlerWithData**: This specialized handler receives custom data from the authority during spawning, enabling dynamic prefab customization. |
| 24 | + |
| 25 | +Netcode will use the `Instantiate` and `Destroy` methods in place of default spawn handlers for the `NetworkObject` used during spawning and despawning. Because the message to instantiate a new `NetworkObject` originates from the [authority](../terms-concepts/authority.md), not all game clients will have the Instantiate method. All non-authority clients will have the instantiate method invoked if the `INetworkPrefabInstanceHandler` implementation is registered with `NetworkPrefabHandler` (`NetworkManager.PrefabHandler`) and the authority spawns the registered/associated `NetworkObject`. |
| 26 | + |
| 27 | +### INetworkPrefabInstanceHandler |
| 28 | + |
| 29 | +This is the simplest prefab handler description. Use the `INetworkPrefabInstanceHandler` for situations where the prefab override behaviour is consistent and known. |
| 30 | + |
| 31 | +```csharp |
| 32 | + public interface INetworkPrefabInstanceHandler |
| 33 | + { |
| 34 | + NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation); |
| 35 | + void Destroy(NetworkObject networkObject); |
| 36 | + } |
| 37 | +``` |
| 38 | + |
| 39 | +### NetworkPrefabInstanceHandlerWithData |
| 40 | + |
| 41 | +the `NetworkPrefabInstanceHandlerWithData` allows for sending custom data from the authority during object spawning. This extra data can then be used to change the behavior of the `Instantiate` method. An implementation of `NetworkPrefabInstanceHandlerWithData` allows for sending any custom type that is serializable using [INetworkSerializable](advanced-topics/serialization/inetworkserializable.md). |
| 42 | + |
| 43 | +```csharp |
| 44 | +public abstract class NetworkPrefabInstanceHandlerWithData<T> : INetworkPrefabInstanceHandlerWithData |
| 45 | + where T : struct, INetworkSerializable |
| 46 | +{ |
| 47 | + public abstract NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation, T instantiationData); |
| 48 | + public abstract void Destroy(NetworkObject networkObject); |
| 49 | +} |
| 50 | +``` |
| 51 | + |
| 52 | +## Prefab handler registration |
| 53 | + |
| 54 | +Once you have created a class to be your prefab handler, you can then register the class with the network prefab handler system using `NetworkManager.PrefabHandler.AddHandler`. Prefab handlers are registered against a NetworkObject's [GlobalObjectIdHash](../basics/networkobject.md#using-networkobjects). |
| 55 | + |
| 56 | +```csharp |
| 57 | +public class GameManager : NetworkBehaviour |
| 58 | +{ |
| 59 | + [SerializeField] private GameObject prefabToSpawn; |
| 60 | + |
| 61 | + void Start() |
| 62 | + { |
| 63 | + var customHandler = new MyPrefabHandler(); |
| 64 | + NetworkManager.PrefabHandler.AddHandler(prefabToSpawn, customHandler); |
| 65 | + } |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +Prefab handlers can be unregistered using `NetworkManager.PrefabHandler.RemoveHandler`. |
| 70 | + |
| 71 | +## Object spawning with prefab handlers |
| 72 | + |
| 73 | +Once a prefab handler is registered Netcode will automatically use the defined `Initialize` and `Destroy` methods to manage the object lifecycle. [Spawn the network prefab as usual](../basics/object-spawning.md#spawning-a-network-prefab-overview) and the `Initialize` method will be called on whichever handler is registered with the spawned network prefab. |
| 74 | + |
| 75 | +Note that the `Initialize` method is only called on non-authority clients. To customize network prefab behavior on the authority, you can use [prefab overrides](../basics/object-spawning.md#taking-prefab-overrides-into-consideration). |
| 76 | + |
| 77 | +### Object spawning with custom data |
| 78 | + |
| 79 | +For handlers that support custom data, the data to send needs to be manually set. To do this, call `SetInstantiationData` before calling the `Spawn` method. If `SetInstantiationData` is not called, the `default` implementation will be sent to the `Instantiate` call. |
| 80 | + |
| 81 | +```csharp |
| 82 | +public struct SpawnData : INetworkSerializable |
| 83 | +{ |
| 84 | + public int version; |
| 85 | + public string name; |
| 86 | + |
| 87 | + public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter |
| 88 | + { |
| 89 | + serializer.SerializeValue(ref version); |
| 90 | + serializer.SerializeValue(ref name); |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +public class CountingObject : NetworkBehaviour |
| 95 | +{ |
| 96 | + [SerializeField] private GameObject prefabToSpawn; |
| 97 | + public string currentName |
| 98 | + |
| 99 | + public void SpawnObject(int objectTypeToSpawn) |
| 100 | + { |
| 101 | + var instance = Instantiate(prefabToSpawn); |
| 102 | + |
| 103 | + // Set data before spawning |
| 104 | + var customSpawnData = new SpawnData { version: objectTypeToSpawn, name: currentName} |
| 105 | + NetworkManager.Singleton.PrefabHandler.SetInstantiationData(instance, customSpawnData); |
| 106 | + |
| 107 | + instance.Spawn(); |
| 108 | + } |
| 109 | +} |
| 110 | +``` |
| 111 | + |
| 112 | +All non-authority clients will then receive this data when `Instantiate` is called. |
| 113 | + |
| 114 | +```csharp |
| 115 | +public class SpawnWithDataSystem : NetworkPrefabInstanceHandlerWithData<SpawnData> |
| 116 | +{ |
| 117 | + public override NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation, SpawnData data) |
| 118 | + { |
| 119 | + // Create the client-side prefab using the spawn data from the authority |
| 120 | + var prefabToSpawn = GetPrefabForVersion(data.version); |
| 121 | + var instance = Instantiate(prefabToSpawn); |
| 122 | + |
| 123 | + var obj = instance.GetComponent<CountingObject>(); |
| 124 | + obj.currentName = data.name; |
| 125 | + |
| 126 | + return instance.GetComponent<NetworkObject>(); |
| 127 | + } |
| 128 | + |
| 129 | + public override void Destroy(NetworkObject networkObject) |
| 130 | + { |
| 131 | + Object.DestroyImmediate(networkObject.gameObject); |
| 132 | + } |
| 133 | + |
| 134 | + private GameObject GetPrefabForVersion(int version) |
| 135 | + { |
| 136 | + // Here you can implement logic to return a different client-side game object |
| 137 | + // depending on the information sent from the server. |
| 138 | + } |
| 139 | +} |
| 140 | +``` |
| 141 | + |
| 142 | +## Further Reading |
| 143 | + |
| 144 | +- [Object pooling](./object-pooling.md) |
| 145 | +- [Authority prefab overrides](../basics/object-spawning.md#taking-prefab-overrides-into-consideration) |
0 commit comments