Skip to content

Commit 1318db3

Browse files
chore: suggested changes for the instantiate with data documentation updates (#3579)
Some suggested updates to the network prefab handler documentation (#3574).
1 parent 5628f23 commit 1318db3

File tree

1 file changed

+117
-34
lines changed

1 file changed

+117
-34
lines changed

com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md

Lines changed: 117 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ Prefab handlers are classes which implement on of the Netcode for GameObjects pr
2222
- **INetworkPrefabInstanceHandler**: This is the simplest interface for custom prefab handlers.
2323
- **NetworkPrefabInstanceHandlerWithData**: This specialized handler receives custom data from the authority during spawning, enabling dynamic prefab customization.
2424

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`.
25+
Netcode will use the `Instantiate` and `Destroy` methods in place of default spawn handlers for the `NetworkObject` used during spawning and de-spawning. The authority instance will use the traditional spawning approach where it will, via user script, instantiate and spawn a network prefab (even for those registered with a prefab handler). However, all non-authority clients will automatically use the instantiate method defined by the `INetworkPrefabInstanceHandler` implementation if the network prefab spawned has a registered `INetworkPrefabInstanceHandler` implementation with the `NetworkPrefabHandler` (`NetworkManager.PrefabHandler`).
2626

2727
### INetworkPrefabInstanceHandler
2828

29-
This is the simplest prefab handler description. Use the `INetworkPrefabInstanceHandler` for situations where the prefab override behaviour is consistent and known.
29+
When all you want to do is handle overriding a network prefab, implementing the `INetworkPrefabInstanceHandler` interface and registering an instance of that implementation with the `NetworkPrefabHandler` (`NetworkManager.PrefabHandler`) is all you would need to do. Use the `INetworkPrefabInstanceHandler` for situations where the prefab override behavior is consistent and known.
3030

3131
```csharp
3232
public interface INetworkPrefabInstanceHandler
@@ -38,7 +38,7 @@ This is the simplest prefab handler description. Use the `INetworkPrefabInstance
3838

3939
### NetworkPrefabInstanceHandlerWithData
4040

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).
41+
If you are looking to provide serialized data to be used during the instantiation process, then the `NetworkPrefabInstanceHandlerWithData` class is what you would want to derive from and register with the `NetworkPrefabHandler` (`NetworkManager.PrefabHandler`). The `NetworkPrefabInstanceHandlerWithData` class 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).
4242

4343
```csharp
4444
public abstract class NetworkPrefabInstanceHandlerWithData<T> : INetworkPrefabInstanceHandlerWithData
@@ -51,7 +51,7 @@ public abstract class NetworkPrefabInstanceHandlerWithData<T> : INetworkPrefabIn
5151

5252
## Prefab handler registration
5353

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).
54+
Once you have created your prefab handler (_derived class or interface implementation_), you will need to register any new instance of your prefab handler with the network prefab handler system using `NetworkManager.PrefabHandler.AddHandler`. Prefab handlers are registered against a NetworkObject's [GlobalObjectIdHash](../basics/networkobject.md#using-networkobjects).
5555

5656
```csharp
5757
public class GameManager : NetworkBehaviour
@@ -66,7 +66,7 @@ public class GameManager : NetworkBehaviour
6666
}
6767
```
6868

69-
Prefab handlers can be unregistered using `NetworkManager.PrefabHandler.RemoveHandler`.
69+
In order to un-register a prefab handler, you can [invoke the `NetworkManager.PrefabHandler.RemoveHandler` method](https://docs.unity3d.com/Packages/[email protected]/api/Unity.Netcode.NetworkPrefabHandler.html#Unity_Netcode_NetworkPrefabHandler_RemoveHandler_System_UInt32_) (_There are several override versions of this method_).
7070

7171
## Object spawning with prefab handlers
7272

@@ -76,68 +76,151 @@ Note that the `Initialize` method is only called on non-authority clients. To cu
7676

7777
### Object spawning with custom data
7878

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.
79+
When using a handler derived from `NetworkPrefabInstanceHandlerWithData`, you must manually set the instantiation data after instantiating the instance but before spawning. To do this, invoke the `NetworkPrefabInstanceHandlerWithData.SetInstantiationData` method before invoking the `NetworkObject.Spawn` method. If `SetInstantiationData` is not called, the `default` implementation will be sent to the `Instantiate` call.
80+
81+
#### Example
82+
83+
Below we can find an example script where the `InstantiateData` structure implements the `INetworkSerializable` interface and it will be used to serialize the instantiation data for the network prefab defined within the below `SpawnPrefabWithColor` NetworkBehaviour.
8084

8185
```csharp
82-
public struct SpawnData : INetworkSerializable
86+
/// <summary>
87+
/// The instantiation data that is serialzied and sent with the
88+
/// spawn object message and provided prior to instantiating.
89+
/// </summary>
90+
public struct InstantiateData : INetworkSerializable
8391
{
84-
public int version;
85-
public string name;
92+
// For example purposes, the color of the material of a MeshRenderer
93+
public Color Color;
94+
95+
// Add additional pre-spawn configuration fields here:
8696
8797
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
8898
{
89-
serializer.SerializeValue(ref version);
90-
serializer.SerializeValue(ref name);
99+
serializer.SerializeValue(ref Color);
100+
101+
// Add addition (field) value serialization here for each new field:
91102
}
92103
}
104+
```
105+
106+
The below `SpawnPrefabWithColor` is a very simple example of a NetworkBehavior component that is to be placed on an in-scene placed NetworkObject. This allows you to configure which network prefab is going to be used to register the `SpawnWithColorSystem`and has a `SpawnWithColorSystem.SpawnObject` method that can be used to instantiate the network prefab instance with instantiation data containing the color to be applied. We can also see that each client/server instance of the `SpawnPrefabWithColor` component will create a `SpawnWithColorHandler` instance.
93107

94-
public class CountingObject : NetworkBehaviour
108+
_While there are much easier ways to synchronize the color of MeshRenderer instances across clients, this is only for example purposes._
109+
110+
```csharp
111+
/// <summary>
112+
/// Add to an in-scene placed <see cref="NetworkObject"/>.
113+
/// </summary>
114+
public class SpawnPrefabWithColor : NetworkBehaviour
95115
{
96-
[SerializeField] private GameObject prefabToSpawn;
97-
public string currentName
116+
/// <summary>
117+
/// The network prefab used to register the <see cref="SpawnWithDataSystem"/> handler.
118+
/// </summary>
119+
public GameObject NetworkPrefab;
98120

99-
public void SpawnObject(int objectTypeToSpawn)
100-
{
101-
var instance = Instantiate(prefabToSpawn);
121+
/// <summary>
122+
/// The <see cref="SpawnWithDataSystem"/> handler instance.
123+
/// </summary>
124+
private SpawnWithColorHandler m_SpawnWithColorHandler;
102125

103-
// Set data before spawning
104-
var customSpawnData = new SpawnData { version: objectTypeToSpawn, name: currentName}
105-
NetworkManager.Singleton.PrefabHandler.SetInstantiationData(instance, customSpawnData);
126+
protected override void OnNetworkPreSpawn(ref NetworkManager networkManager)
127+
{
128+
m_SpawnWithColorHandler = new SpawnWithColorHandler(networkManager, NetworkPrefab);
129+
base.OnNetworkPreSpawn(ref networkManager);
130+
}
106131

107-
instance.Spawn();
132+
/// <summary>
133+
/// Invoked by some other component or additional script logic controls
134+
/// when an object is spawned.
135+
/// </summary>
136+
/// <param name="color">The color to apply (pseudo example purposes)</param>
137+
/// <returns>The spawned <see cref="NetworkObject"/> instance</returns>
138+
public NetworkObject SpawnObject(Vector3 position, Quaternion rotation, Color color)
139+
{
140+
if (!IsSpawned || !HasAuthority || m_SpawnWithColorHandler == null)
141+
{
142+
return null;
143+
}
144+
// Instantiate, set the instantiation data, and then spawn the network prefab.
145+
return m_SpawnWithColorHandler.InstantiateSetDataAndSpawn(position, rotation, new InstantiateData() { Color = color });
108146
}
109147
}
110148
```
149+
Above, we can see the `SpawnWithColorSystem` invokes the `SpawnWithColorHandler.InstantiateSetDataAndSpawn` method to create a new network prefab instance, set the instantiation data, and then spawn the network prefab instance.
111150

112-
All non-authority clients will then receive this data when `Instantiate` is called.
151+
Below we will find the more complex of the three scripts for this example. The `SpawnWithColorHandler` constructor automatically registers itself with the `NetworkManager`.
113152

114153
```csharp
115-
public class SpawnWithDataSystem : NetworkPrefabInstanceHandlerWithData<SpawnData>
154+
/// <summary>
155+
/// The prefan instance handler that uses instantiation data to handle updating
156+
/// the instance's <see cref="MeshRenderer"/>s material's color. (example purposes only)
157+
/// </summary>
158+
public class SpawnWithColorHandler : NetworkPrefabInstanceHandlerWithData<InstantiateData>
116159
{
117-
public override NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation, SpawnData data)
160+
private GameObject m_RegisteredPrefabToSpawn;
161+
private NetworkManager m_NetworkManager;
162+
163+
/// <summary>
164+
/// Constructor
165+
/// </summary>
166+
/// <param name="registeredPrefab">The prefab used to register this handler instance.</param>
167+
/// <param name="networkPrefabGroup">The prefab group this handler will be able to spawn.</param>
168+
public SpawnWithColorSystem(NetworkManager networkManager, GameObject registeredPrefab)
118169
{
119-
// Create the client-side prefab using the spawn data from the authority
120-
var prefabToSpawn = GetPrefabForVersion(data.version);
121-
var instance = Instantiate(prefabToSpawn);
170+
m_NetworkManager = networkManager;
171+
m_RegisteredPrefabToSpawn = registeredPrefab;
172+
173+
// Register this handler with the NetworkPrefabHandler
174+
m_NetworkManager.PrefabHandler.AddHandler(m_RegisteredPrefabToSpawn, this);
175+
}
176+
177+
/// <summary>
178+
/// Used by the server or a client when using a distributed authority network topology,
179+
/// instantiate the prefab, set the instantiation data, and then spawn.
180+
/// </summary>
181+
public NetworkObject InstantiateSetDataAndSpawn(Vector3 position, Quaternion rotation, InstantiateData instantiateData)
182+
{
183+
var instance = GetPrefabInstance(position, rotation, instantiateData);
122184

123-
var obj = instance.GetComponent<CountingObject>();
124-
obj.currentName = data.name;
185+
// Set the spawndata before spawning
186+
m_NetworkManager.PrefabHandler.SetInstantiationData(instance, instantiateData);
187+
188+
instance.GetComponent<NetworkObject>().Spawn();
189+
return instance;
190+
}
191+
192+
/// <summary>
193+
/// Returns an instance of the registered prefab (no instantiation data set yet)
194+
/// </summary>
195+
public NetworkObject GetPrefabInstance(Vector3 position, Quaternion rotation, InstantiateData instantiateData)
196+
{
197+
// Optional to include your own position and/or rotation within the InstantiateData.
198+
var instance = Object.Instantiate(m_RegisteredPrefabToSpawn, position, rotation);
199+
var meshRenderers = instance.GetComponentsInChildren<MeshRenderer>();
200+
foreach (var renderer in meshRenderers)
201+
{
202+
// Assign the color to each MeshRenderer (just a psuedo example)
203+
renderer.material.color = instantiateData.Color;
204+
}
125205

126206
return instance.GetComponent<NetworkObject>();
127207
}
128208

129-
public override void Destroy(NetworkObject networkObject)
209+
/// <inheritdoc/>
210+
public override NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation, InstantiateData instantiateData)
130211
{
131-
Object.DestroyImmediate(networkObject.gameObject);
212+
// For non-authority instances, we can just get an instance based off of the passed in InstantiateData
213+
return GetPrefabInstance(position, rotation, instantiateData);
132214
}
133215

134-
private GameObject GetPrefabForVersion(int version)
216+
/// <inheritdoc/>
217+
public override void Destroy(NetworkObject networkObject)
135218
{
136-
// Here you can implement logic to return a different client-side game object
137-
// depending on the information sent from the server.
219+
Object.DestroyImmediate(networkObject.gameObject);
138220
}
139221
}
140222
```
223+
When instantiating from user script for a host, server, or distributed authority client, the above `InstantiateSetDataAndSpawn` method is used. When instantiating on non-authority instances the `GetPrefabInstance` is used since the authority provides the instantiation data.
141224

142225
## Further Reading
143226

0 commit comments

Comments
 (0)