From f5a3b7dfc4153a71aeb0b6b725337f2dfb736603 Mon Sep 17 00:00:00 2001 From: Emma Date: Fri, 1 Aug 2025 17:55:05 -0400 Subject: [PATCH 1/8] Add network prefab handler documentation --- .../Documentation~/TableOfContents.md | 3 +- .../advanced-topics/network-prefab-handler.md | 145 ++++++++++++++++++ .../advanced-topics/object-pooling.md | 14 +- 3 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md diff --git a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md index f784cc5148..bf749ce60b 100644 --- a/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md +++ b/com.unity.netcode.gameobjects/Documentation~/TableOfContents.md @@ -34,6 +34,7 @@ * [Ownership race conditions](basics/race-conditions.md) * [Spawning and despawning](spawn-despawn.md) * [Object spawning](basics/object-spawning.md) + * [Network prefab handler](advanced-topics/network-prefab-handler.md) * [Object pooling](advanced-topics/object-pooling.md) * [Object visibility](basics/object-visibility.md) * [Spawning synchronization](basics/spawning-synchronization.md) @@ -100,4 +101,4 @@ * [NetworkObject parenting](samples/bossroom/networkobject-parenting.md) * [Optimizing Boss Room](samples/bossroom/optimizing-bossroom.md) * [NetworkRigidbody](samples/bossroom/networkrigidbody.md) - * [Spawn NetworkObjects](samples/bossroom/spawn-networkobjects.md) \ No newline at end of file + * [Spawn NetworkObjects](samples/bossroom/spawn-networkobjects.md) diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md new file mode 100644 index 0000000000..5adfb60839 --- /dev/null +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md @@ -0,0 +1,145 @@ +# Network prefab handler + +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. + +The network prefab handler system is accessible from the [NetworkManager](../components/networkmanager.md) as `NetworkManager.PrefabHandler`. + +## Overview + +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: + +- **Object pooling**: Reusing objects to reduce memory allocation and initialization costs. +- **Performance optimization**: Using different prefab variants on different platforms (e.g. using a simpler object for server simulation). +- **Custom initialization**: Setting up objects with game client specific data or configurations. +- **Conditional spawning**: Initializing different prefab variants based on runtime conditions. + +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. + +## Creating a prefab handler + +Prefab handlers are classes which implement on of the Netcode for GameObjects prefab handler descriptions. There are currently two such descriptions: + +- **INetworkPrefabInstanceHandler**: This is the simplest interface for custom prefab handlers. +- **NetworkPrefabInstanceHandlerWithData**: This specialized handler receives custom data from the authority during spawning, enabling dynamic prefab customization. + +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`. + +### INetworkPrefabInstanceHandler + +This is the simplest prefab handler description. Use the `INetworkPrefabInstanceHandler` for situations where the prefab override behaviour is consistent and known. + +```csharp + public interface INetworkPrefabInstanceHandler + { + NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation); + void Destroy(NetworkObject networkObject); + } +``` + +### NetworkPrefabInstanceHandlerWithData + +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). + +```csharp +public abstract class NetworkPrefabInstanceHandlerWithData : INetworkPrefabInstanceHandlerWithData + where T : struct, INetworkSerializable +{ + public abstract NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation, T instantiationData); + public abstract void Destroy(NetworkObject networkObject); +} +``` + +## Prefab handler registration + +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). + +```csharp +public class GameManager : NetworkBehaviour +{ + [SerializeField] private GameObject prefabToSpawn; + + void Start() + { + var customHandler = new MyPrefabHandler(); + NetworkManager.PrefabHandler.AddHandler(prefabToSpawn, customHandler); + } +} +``` + +Prefab handlers can be unregistered using `NetworkManager.PrefabHandler.RemoveHandler`. + +## Object spawning with prefab handlers + +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. + +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). + +### Object spawning with custom data + +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. + +```csharp +public struct SpawnData : INetworkSerializable +{ + public int version; + public string name; + + public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter + { + serializer.SerializeValue(ref version); + serializer.SerializeValue(ref name); + } +} + +public class CountingObject : NetworkBehaviour +{ + [SerializeField] private GameObject prefabToSpawn; + public string currentName + + public void SpawnObject(int objectTypeToSpawn) + { + var instance = Instantiate(prefabToSpawn); + + // Set data before spawning + var customSpawnData = new SpawnData { version: objectTypeToSpawn, name: currentName} + NetworkManager.Singleton.PrefabHandler.SetInstantiationData(instance, customSpawnData); + + instance.Spawn(); + } +} +``` + +All non-authority clients will then receive this data when `Instantiate` is called. + +```csharp +public class SpawnWithDataSystem : NetworkPrefabInstanceHandlerWithData +{ + public override NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation, SpawnData data) + { + // Create the client-side prefab using the spawn data from the authority + var prefabToSpawn = GetPrefabForVersion(data.version); + var instance = Instantiate(prefabToSpawn); + + var obj = instance.GetComponent(); + obj.currentName = data.name; + + return instance.GetComponent(); + } + + public override void Destroy(NetworkObject networkObject) + { + Object.DestroyImmediate(networkObject.gameObject); + } + + private GameObject GetPrefabForVersion(int version) + { + // Here you can implement logic to return a different client-side game object + // depending on the information sent from the server. + } +} +``` + +## Further Reading + +- [Object pooling](./object-pooling.md) +- [Authority prefab overrides](../basics/object-spawning.md#taking-prefab-overrides-into-consideration) diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/object-pooling.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/object-pooling.md index 32e7add573..1fc4ea9279 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/object-pooling.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/object-pooling.md @@ -4,17 +4,7 @@ Netcode for GameObjects (Netcode) provides built-in support for Object Pooling, See [Introduction to Object Pooling](https://learn.unity.com/tutorial/introduction-to-object-pooling) to learn more about the importance of pooling objects. -## NetworkPrefabInstanceHandler - -You can register your own spawn handlers by including the `INetworkPrefabInstanceHandler` interface and registering with the `NetworkPrefabHandler`. -```csharp - public interface INetworkPrefabInstanceHandler - { - NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation); - void Destroy(NetworkObject networkObject); - } -``` -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 a Host or Server, both won't have the Instantiate method invoked. All clients (excluding a Host) will have the instantiate method invoked if the `INetworkPrefabInstanceHandler` implementation is registered with `NetworkPrefabHandler` (`NetworkManager.PrefabHandler`) and a Host or Server spawns the registered/associated `NetworkObject`. +To learn how to override the default Netcode destroy and spawn handlers, see the [Network prefab handler](./network-prefab-handler.md). \ No newline at end of file +--> From 85d68453c858e713ef297b0c4f6ac8ec765d6bbe Mon Sep 17 00:00:00 2001 From: Emma Date: Fri, 1 Aug 2025 17:55:27 -0400 Subject: [PATCH 2/8] Update object spawning page to be distributed authority compatible --- .../Documentation~/basics/object-spawning.md | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md b/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md index ffab9107fa..dd6879e763 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md @@ -1,6 +1,6 @@ # Object spawning -In Unity, you typically create a new game object using the `Instantiate` function. Creating a game object with `Instantiate` will only create that object on the local machine. `Spawning` in Netcode for GameObjects (Netcode) means to instantiate and/or spawn the object that is synchronized between all clients by the server. +In Unity, you typically create a new game object using the `Instantiate` function. Creating a game object with `Instantiate` will only create that object on the local machine. `Spawning` in Netcode for GameObjects (Netcode) means to instantiate and/or spawn the object that is synchronized between all game clients. ## Network Prefabs @@ -26,14 +26,16 @@ There are four steps to registering a network Prefab with a `NetworkManager`: ### Spawning a Network Prefab (Overview) -Netcode uses a server authoritative networking model so spawning netcode objects can only be done on a server or host. To spawn a network prefab, you must first create an instance of the network Prefab and then invoke the spawn method on the `NetworkObject` component of the instance you created. +When using a [server authoritative networking model](../terms-concepts/authority/#server-authority) only the server or host can spawn netcode objects. Under a [distributed authority networking model](..terms-concepts/authority/#distributed-authority), any game client can spawn netcode objects. The game client that spawned the network object then becomes the [authority](../terms-concepts/authority.md) of that object. + +To spawn a network prefab, you must first create an instance of the network Prefab and then invoke the spawn method on the `NetworkObject` component of the instance you created. _In most cases, you will want to keep the `NetworkObject` component attached to the root `GameObject` of the network prefab._ -By default a newly spawned network Prefab instance is owned by the server unless otherwise specified. +By default a newly spawned network Prefab instance is owned by the authority unless otherwise specified. See [Ownership](networkobject.md#ownership) for more information. -The following is a basic example of how to spawn a network Prefab instance (with the default server ownership): +The following is a basic example of how to spawn a network Prefab instance: ```csharp var instance = Instantiate(myPrefab); @@ -42,6 +44,7 @@ instanceNetworkObject.Spawn(); ``` The `NetworkObject.Spawn` method takes 1 optional parameter that defaults to `true`: + ```csharp public void Spawn(bool destroyWithScene = true); ``` @@ -54,11 +57,13 @@ When you set the destroyWithScene property to `false` it will be treated the sam > You might find it useful to add a `GameObject` property in a `NetworkBehaviour`-derived component to use when assigning a network prefab instance for dynamically spawning. You need to make sure to instantiate a new instance **prior** to spawning. If you attempt to just spawn the actual network prefab instance it can result in unexpected results. ### Taking Prefab Overrides Into Consideration -Sometimes, you might want to make a simpler prefab instance to be spawned on server version the override for clients. You should take this into consideration when dynamically spawning a network prefab. If you're running as a host, you want the override to spawn since a host is both a server and a client. However, if you also want to have the ability to run as a dedicated server, you might want to spawn the source network prefab. + +Sometimes, you might want to use a different prefab instance on the authority compare to other clients. You should take this into consideration when dynamically spawning a network prefab. If you're running as a host, you want the override to spawn since a host is both a server and a client. However, if you also want to have the ability to run as a dedicated server, you might want to spawn the source network prefab. There are two ways you can accomplish this, as explained below. #### Get The Network Prefab Override First + This option provides you with the overall view of getting the network prefab override, instantiating it, and then spawning it. ```csharp @@ -66,15 +71,18 @@ var instance = Instantiate(NetworkManager.GetNetworkPrefabOverride(myPrefab)); var instanceNetworkObject = instance.GetComponent(); instanceNetworkObject.Spawn(); ``` + In the above script, we get the prefab override using the `NetworkManager.GetNetworkPrefabOverride` method. Then we create an instance of the network prefab override, and finally we spawn the network prefab override instance's `NetworkObject`. #### Using InstantiateAndSpawn + The second option is to leverage the `NetworkSpawnManager.InstantiateAndSpawn` method that handles whether or not to spawn an override for you. The below script is written as if it's being invoked within a `NetworkBehaviour`. ```csharp var networkObject = NetworkManager.SpawnManager.InstantiateAndSpawn(myPrefab, ownerId); ``` -We pass in the overridden source network prefab we want to have instantiated and spawned, and then it returns the instantiated and spawned `NetworkObject` of the spawned object. The default behavior of `InstantiateAndSpawn` is to spawn the override if running as a host and the original source prefab if running as a server. + +We pass in the overridden source network prefab we want to have instantiated and spawned, and then it returns the instantiated and spawned `NetworkObject` of the spawned object. The default behavior of `InstantiateAndSpawn` is to spawn the original source prefab if running as a server and the override otherwise. `InstantiateAndSpawn` has several parameters to provide more control over this process: @@ -84,14 +92,15 @@ InstantiateAndSpawn(NetworkObject networkPrefab, ulong ownerClientId = NetworkMa Looking at the parameters, we can see it defaults to the server as the owner, ensures that the instantiated `NetworkObject` won't be destroyed if the scene is unloaded, is not spawned as a player, has a `forceOverride` parameter, and provides a way to set the position and rotation of the newly instantiated `NetworkObject`. -The `forceOverride` parameter, when set to true, will use the override whether you're running as either server or host. +The `forceOverride` parameter, when set to true, will always use the override. +To override prefabs on non-authority game clients, see the [Network prefab handler](./network-prefab-handler.md). ## Destroying / Despawning -By default, a spawned network Prefab instance that is destroyed on the server/host will be automatically destroyed on all clients. +By default, a spawned network Prefab instance that is destroyed on the authority will be automatically destroyed on all clients. -When a client disconnects, all network Prefab instances created during the network session will be destroyed on the client-side by default. If you don't want that to happen, set the `DontDestroyWithOwner` field on `NetworkObject` to true before despawning. +When a client disconnects, all network Prefab instances dynamically created during the network session will be destroyed on the client-side by default. If you don't want that to happen, set the `DontDestroyWithOwner` field on `NetworkObject` to true before despawning. To do this at runtime: @@ -108,9 +117,9 @@ As an alternative way, you can make the `NetworkObject.DontDestroyWithOwner` pro ### Despawning -Only a server can despawn a `NetworkObject`, and the default despawn behavior is to destroy the associated GameObject. to despawn but not destroy a `NetworkObject`, you should call `NetworkObject.Despawn` and pass false as the parameter. Clients will always be notified and will mirror the despawn behavior. If you despawn and destroy on the server then all clients will despawn and then destroy the `GameObject` that the `NetworkObjet` component is attached to. +Only the authority can despawn a `NetworkObject`, and the default despawn behavior is to destroy the associated GameObject. to despawn but not destroy a `NetworkObject`, you should call `NetworkObject.Despawn` and pass false as the parameter. Non-authority clients will always be notified and will mirror the despawn behavior. If you despawn and destroy on the authority then all other connected clients will despawn and then destroy the `GameObject` that the `NetworkObjet` component is attached to. -On the client side, you should never call `Object.Destroy` on any `GameObject` with a `NetworkObject` component attached to it (this isn't supported and will cause an exception to be thrown). If you want to use a more client authority model, have the client with ownership invoke an RPC to defer the despawning on server side. +On the non-authority side, you should never call `Object.Destroy` on any `GameObject` with a `NetworkObject` component attached to it (this isn't supported and will cause an exception to be thrown). To allow non-authority clients to destroy objects they do not own, have the relevant client invoke an RPC to defer the despawning on the authority side. The only way to despawn `NetworkObject` for a specific client is to use `NetworkObject.NetworkHide`. See: [Object Visibility](object-visibility.md) for more information on this. @@ -136,8 +145,8 @@ This type of dynamically spawned `NetworkObject` typically is a simple wrapper c public override void OnNetworkSpawn() { - // Only the server spawns, clients will disable this component on their side - enabled = IsServer; + // Only the authority spawns, other clients will disable this component on their side + enabled = HasAuthority; if (!enabled || PrefabToSpawn == null) { return; @@ -156,7 +165,7 @@ This type of dynamically spawned `NetworkObject` typically is a simple wrapper c public override void OnNetworkDespawn() { - if (IsServer && DestroyWithSpawner && m_SpawnedNetworkObject != null && m_SpawnedNetworkObject.IsSpawned) + if (HasAuthority && DestroyWithSpawner && m_SpawnedNetworkObject != null && m_SpawnedNetworkObject.IsSpawned) { m_SpawnedNetworkObject.Despawn(); } @@ -171,11 +180,11 @@ Consumable and/or items that can be picked up by a player or NPC(that is, a weap > While the NonPooledDynamicSpawner example is one of the simplest ways to spawn a NetworkObject, there is a memory allocation cost associated with instantiating and destroying the GameObject and all attached components. This design pattern can sometimes be all you need for the netcode game asset you are working with, and other times you might want to respawn/re-use the object instance. When performance is a concern and you want to spawn more than just one `NetworkObject` during the lifetime of the spawner or want to repeatedly respawn a single `NetworkObject`, the less proccessor and memory allocation intensive technique is to use [pooled dynamic spawning](#pooled-dynamic-spawning). > [!NOTE] -> Really, when we use the term "non-pooled" more often than not we are referring to the concept that a `GameObject` will be instantiated on both the server and the clients each time an instance is spawned. +> Really, when we use the term "non-pooled" more often than not we are referring to the concept that a `GameObject` will be instantiated on all game clients each time an instance is spawned. ### Pooled Dynamic Spawning -Pooled dynamic spawning is when netcode objects (`GameObject` with one `NetworkObject` component) aren't destroyed on the server or the client when despawned. Instead, specific components are just disabled (or the `GameObject` itself) when a netcode object is despawned. A pooled dynamically spawned netcode object is typically instantiated during an already memory allocation heavy period of time (like when a scene is loaded or even at the start of your application before even establishing a network connection). Pooled dynamically spawned netcode objects are more commonly thought of as more than one netcode object that can be re-used without incurring the memory allocation and initialization costs. However, you might also run into scenarios where you need just one dynamically spawned netcode object to be treated like a pooled dynmically spawned netcode object. +Pooled dynamic spawning is when netcode objects (`GameObject` with one `NetworkObject` component) aren't destroyed on game clients when despawned. Instead, specific components are just disabled (or the `GameObject` itself) when a netcode object is despawned. A pooled dynamically spawned netcode object is typically instantiated during an already memory allocation heavy period of time (like when a scene is loaded or even at the start of your application before even establishing a network connection). Pooled dynamically spawned netcode objects are more commonly thought of as more than one netcode object that can be re-used without incurring the memory allocation and initialization costs. However, you might also run into scenarios where you need just one dynamically spawned netcode object to be treated like a pooled dynmically spawned netcode object. Fortunately, Netcode for GameObjects provides you with a way to be in control over the instatiation and destruction process for one or many netcode objects by via the `INetworkPrefabInstanceHandler` interface. Any `INetworkPrefabInstanceHandler`implementation should be registered with the `NetworkPrefabHandler`(for multiple netcode objects see [Object Pooling](../advanced-topics/object-pooling.md)) to accomplish this. @@ -193,7 +202,7 @@ public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstan private void Start() { - // Instantiate our instance when we start (for both clients and server) + // Instantiate our instance when we start (for all connected game clients) m_PrefabInstance = Instantiate(PrefabToSpawn); // Get the NetworkObject component assigned to the Prefab instance @@ -219,7 +228,7 @@ public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstan } /// - /// Invoked only on clients and not server or host + /// Invoked only on non-authority clients /// INetworkPrefabInstanceHandler.Instantiate implementation /// Called when Netcode for GameObjects need an instance to be spawned /// @@ -232,7 +241,7 @@ public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstan } /// - /// Client and Server side + /// Called on all game clients /// INetworkPrefabInstanceHandler.Destroy implementation /// public void Destroy(NetworkObject networkObject) @@ -242,7 +251,7 @@ public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstan public void SpawnInstance() { - if (!IsServer) + if (!HasAuthority) { return; } @@ -261,7 +270,7 @@ public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstan // INetworkPrefabInstanceHandler interface with the Prefab handler NetworkManager.PrefabHandler.AddHandler(PrefabToSpawn, this); - if (!IsServer || !SpawnPrefabAutomatically) + if (!HasAuthority || !SpawnPrefabAutomatically) { return; } @@ -318,7 +327,7 @@ This reduces the complexity down to setting the SpawnedComponents `GameObject` t ## In-Scene Placed `NetworkObject` -Any objects in the scene with active and spawned `NetworkObject` components will get automatically replicated by Netcode. There is no need to manually spawn them when scene management is enabled in the `NetworkManager`. In-scene placed `NetworkObjects` should typically be used like a "static" netcode object, where the netcode object is typically spawned upon the scene being loaded on the server-side and synchronized with clients once they finish loading the same scene. +Any objects in the scene with active and spawned `NetworkObject` components will get automatically replicated by Netcode. There is no need to manually spawn them when scene management is enabled in the `NetworkManager`. In-scene placed `NetworkObjects` should typically be used like a "static" netcode object, where the netcode object is typically spawned upon the scene being loaded on the authority-side and synchronized with other clients once they finish loading the same scene. [Learn more about In-Scene Placed `NetworkObjects`](scenemanagement/inscene-placed-networkobjects.md) From 5628f23efe033b700f7fee18c2dde112b98e3576 Mon Sep 17 00:00:00 2001 From: Emma Date: Fri, 1 Aug 2025 18:01:44 -0400 Subject: [PATCH 3/8] Update CHANGELOG --- com.unity.netcode.gameobjects/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 489af4f446..b1be6e5682 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -13,7 +13,7 @@ Additional documentation and release notes are available at [Multiplayer Documen - Added serializer for `Pose` (#3546) - Added methods `GetDefaultNetworkSettings` and `GetDefaultPipelineConfigurations` to `UnityTransport`. These can be used to retrieve the default settings and pipeline stages that are used by `UnityTransport`. This is useful when providing a custom driver constructor through `UnityTransport.s_DriverConstructor`, since it allows reusing or tuning the existing configuration instead of trying to recreate it. This means a transport with a custom driver can now easily benefit from most of the features of `UnityTransport`, like integration with the Network Simulator and Network Profiler from the multiplayer tools package. (#3501) - Added mappings between `ClientId` and `TransportId`. (#3516) -- Added `NetworkPrefabInstanceHandlerWithData`, a variant of `INetworkPrefabInstanceHandler` that provides access to custom instantiation data directly within the `Instantiate()` method. (#3430) +- Added `NetworkPrefabInstanceHandlerWithData`, a variant of `INetworkPrefabInstanceHandler` that provides access to custom instantiation data directly within the `Instantiate()` method. (#3497) ### Fixed From 1318db388e16930f1e0ba3413b2efa7cb54360e2 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Tue, 5 Aug 2025 14:20:50 -0500 Subject: [PATCH 4/8] chore: suggested changes for the instantiate with data documentation updates (#3579) Some suggested updates to the network prefab handler documentation (#3574). --- .../advanced-topics/network-prefab-handler.md | 151 ++++++++++++++---- 1 file changed, 117 insertions(+), 34 deletions(-) diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md index 5adfb60839..47331d1d37 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md @@ -22,11 +22,11 @@ Prefab handlers are classes which implement on of the Netcode for GameObjects pr - **INetworkPrefabInstanceHandler**: This is the simplest interface for custom prefab handlers. - **NetworkPrefabInstanceHandlerWithData**: This specialized handler receives custom data from the authority during spawning, enabling dynamic prefab customization. -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`. +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`). ### INetworkPrefabInstanceHandler -This is the simplest prefab handler description. Use the `INetworkPrefabInstanceHandler` for situations where the prefab override behaviour is consistent and known. +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. ```csharp public interface INetworkPrefabInstanceHandler @@ -38,7 +38,7 @@ This is the simplest prefab handler description. Use the `INetworkPrefabInstance ### NetworkPrefabInstanceHandlerWithData -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). +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). ```csharp public abstract class NetworkPrefabInstanceHandlerWithData : INetworkPrefabInstanceHandlerWithData @@ -51,7 +51,7 @@ public abstract class NetworkPrefabInstanceHandlerWithData : INetworkPrefabIn ## Prefab handler registration -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). +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). ```csharp public class GameManager : NetworkBehaviour @@ -66,7 +66,7 @@ public class GameManager : NetworkBehaviour } ``` -Prefab handlers can be unregistered using `NetworkManager.PrefabHandler.RemoveHandler`. +In order to un-register a prefab handler, you can [invoke the `NetworkManager.PrefabHandler.RemoveHandler` method](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.4/api/Unity.Netcode.NetworkPrefabHandler.html#Unity_Netcode_NetworkPrefabHandler_RemoveHandler_System_UInt32_) (_There are several override versions of this method_). ## Object spawning with prefab handlers @@ -76,68 +76,151 @@ Note that the `Initialize` method is only called on non-authority clients. To cu ### Object spawning with custom data -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. +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. + +#### Example + +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. ```csharp -public struct SpawnData : INetworkSerializable +/// +/// The instantiation data that is serialzied and sent with the +/// spawn object message and provided prior to instantiating. +/// +public struct InstantiateData : INetworkSerializable { - public int version; - public string name; + // For example purposes, the color of the material of a MeshRenderer + public Color Color; + + // Add additional pre-spawn configuration fields here: public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { - serializer.SerializeValue(ref version); - serializer.SerializeValue(ref name); + serializer.SerializeValue(ref Color); + + // Add addition (field) value serialization here for each new field: } } +``` + +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. -public class CountingObject : NetworkBehaviour +_While there are much easier ways to synchronize the color of MeshRenderer instances across clients, this is only for example purposes._ + +```csharp +/// +/// Add to an in-scene placed . +/// +public class SpawnPrefabWithColor : NetworkBehaviour { - [SerializeField] private GameObject prefabToSpawn; - public string currentName + /// + /// The network prefab used to register the handler. + /// + public GameObject NetworkPrefab; - public void SpawnObject(int objectTypeToSpawn) - { - var instance = Instantiate(prefabToSpawn); + /// + /// The handler instance. + /// + private SpawnWithColorHandler m_SpawnWithColorHandler; - // Set data before spawning - var customSpawnData = new SpawnData { version: objectTypeToSpawn, name: currentName} - NetworkManager.Singleton.PrefabHandler.SetInstantiationData(instance, customSpawnData); + protected override void OnNetworkPreSpawn(ref NetworkManager networkManager) + { + m_SpawnWithColorHandler = new SpawnWithColorHandler(networkManager, NetworkPrefab); + base.OnNetworkPreSpawn(ref networkManager); + } - instance.Spawn(); + /// + /// Invoked by some other component or additional script logic controls + /// when an object is spawned. + /// + /// The color to apply (pseudo example purposes) + /// The spawned instance + public NetworkObject SpawnObject(Vector3 position, Quaternion rotation, Color color) + { + if (!IsSpawned || !HasAuthority || m_SpawnWithColorHandler == null) + { + return null; + } + // Instantiate, set the instantiation data, and then spawn the network prefab. + return m_SpawnWithColorHandler.InstantiateSetDataAndSpawn(position, rotation, new InstantiateData() { Color = color }); } } ``` +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. -All non-authority clients will then receive this data when `Instantiate` is called. +Below we will find the more complex of the three scripts for this example. The `SpawnWithColorHandler` constructor automatically registers itself with the `NetworkManager`. ```csharp -public class SpawnWithDataSystem : NetworkPrefabInstanceHandlerWithData +/// +/// The prefan instance handler that uses instantiation data to handle updating +/// the instance's s material's color. (example purposes only) +/// +public class SpawnWithColorHandler : NetworkPrefabInstanceHandlerWithData { - public override NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation, SpawnData data) + private GameObject m_RegisteredPrefabToSpawn; + private NetworkManager m_NetworkManager; + + /// + /// Constructor + /// + /// The prefab used to register this handler instance. + /// The prefab group this handler will be able to spawn. + public SpawnWithColorSystem(NetworkManager networkManager, GameObject registeredPrefab) { - // Create the client-side prefab using the spawn data from the authority - var prefabToSpawn = GetPrefabForVersion(data.version); - var instance = Instantiate(prefabToSpawn); + m_NetworkManager = networkManager; + m_RegisteredPrefabToSpawn = registeredPrefab; + + // Register this handler with the NetworkPrefabHandler + m_NetworkManager.PrefabHandler.AddHandler(m_RegisteredPrefabToSpawn, this); + } + + /// + /// Used by the server or a client when using a distributed authority network topology, + /// instantiate the prefab, set the instantiation data, and then spawn. + /// + public NetworkObject InstantiateSetDataAndSpawn(Vector3 position, Quaternion rotation, InstantiateData instantiateData) + { + var instance = GetPrefabInstance(position, rotation, instantiateData); - var obj = instance.GetComponent(); - obj.currentName = data.name; + // Set the spawndata before spawning + m_NetworkManager.PrefabHandler.SetInstantiationData(instance, instantiateData); + + instance.GetComponent().Spawn(); + return instance; + } + + /// + /// Returns an instance of the registered prefab (no instantiation data set yet) + /// + public NetworkObject GetPrefabInstance(Vector3 position, Quaternion rotation, InstantiateData instantiateData) + { + // Optional to include your own position and/or rotation within the InstantiateData. + var instance = Object.Instantiate(m_RegisteredPrefabToSpawn, position, rotation); + var meshRenderers = instance.GetComponentsInChildren(); + foreach (var renderer in meshRenderers) + { + // Assign the color to each MeshRenderer (just a psuedo example) + renderer.material.color = instantiateData.Color; + } return instance.GetComponent(); } - public override void Destroy(NetworkObject networkObject) + /// + public override NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation, InstantiateData instantiateData) { - Object.DestroyImmediate(networkObject.gameObject); + // For non-authority instances, we can just get an instance based off of the passed in InstantiateData + return GetPrefabInstance(position, rotation, instantiateData); } - private GameObject GetPrefabForVersion(int version) + /// + public override void Destroy(NetworkObject networkObject) { - // Here you can implement logic to return a different client-side game object - // depending on the information sent from the server. + Object.DestroyImmediate(networkObject.gameObject); } } ``` +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. ## Further Reading From 064b3c22265bd915d3810ff6fcbf59d92b1cfb74 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity Date: Tue, 5 Aug 2025 14:35:15 -0500 Subject: [PATCH 5/8] update Some additional adjustments and additions. --- .../advanced-topics/network-prefab-handler.md | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md index 47331d1d37..1989e8838b 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md @@ -78,13 +78,13 @@ Note that the `Initialize` method is only called on non-authority clients. To cu 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. -#### Example +#### Simple Example -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. +Below we can find a simple "pseudo" 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. ```csharp /// -/// The instantiation data that is serialzied and sent with the +/// The instantiation data that is serialized and sent with the /// spawn object message and provided prior to instantiating. /// public struct InstantiateData : INetworkSerializable @@ -103,7 +103,7 @@ public struct InstantiateData : INetworkSerializable } ``` -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. +The below `SpawnPrefabWithColor` is an example of a NetworkBehavior component, to be placed on an in-scene placed NetworkObject, that handles the instantiation of a prefab handler (`SpawnWithColorHandler`) and the means to configure the network prefab to register with the handler. It also has a `SpawnWithColorSystem.SpawnObject` method that can be used to instantiate an instance of the assigned network prefab instance that will also have instantiation data associated with it that contains the color to be applied to the instance's `MeshRenderer`s. _While there are much easier ways to synchronize the color of MeshRenderer instances across clients, this is only for example purposes._ @@ -148,7 +148,7 @@ public class SpawnPrefabWithColor : NetworkBehaviour ``` 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. -Below we will find the more complex of the three scripts for this example. The `SpawnWithColorHandler` constructor automatically registers itself with the `NetworkManager`. +Below we will find the slightly more complex, of the three scripts, `SpawnWithColorHandler` with a constructor that automatically registers itself and the network prefab with the `NetworkManager.PrefabHandler`: ```csharp /// @@ -222,6 +222,35 @@ public class SpawnWithColorHandler : NetworkPrefabInstanceHandlerWithData Date: Tue, 5 Aug 2025 15:33:03 -0500 Subject: [PATCH 6/8] style Removing white spaces at the end of a line. Fixed some spelling and text related issues. --- .../advanced-topics/network-prefab-handler.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md index 1989e8838b..99ead5ffed 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md @@ -78,7 +78,7 @@ Note that the `Initialize` method is only called on non-authority clients. To cu 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. -#### Simple Example +#### Simple Example Below we can find a simple "pseudo" 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. @@ -103,7 +103,7 @@ public struct InstantiateData : INetworkSerializable } ``` -The below `SpawnPrefabWithColor` is an example of a NetworkBehavior component, to be placed on an in-scene placed NetworkObject, that handles the instantiation of a prefab handler (`SpawnWithColorHandler`) and the means to configure the network prefab to register with the handler. It also has a `SpawnWithColorSystem.SpawnObject` method that can be used to instantiate an instance of the assigned network prefab instance that will also have instantiation data associated with it that contains the color to be applied to the instance's `MeshRenderer`s. +The below `SpawnPrefabWithColor` is an example of a NetworkBehavior component, to be placed on an in-scene placed NetworkObject, that handles the instantiation of a prefab handler (`SpawnWithColorHandler`) and the means to configure the network prefab to register with the handler. It also has a `SpawnWithColorSystem.SpawnObject` method that can be used to instantiate an instance of the assigned network prefab instance that will also have instantiation data associated with it that contains the color to be applied to the instance's `MeshRenderer`s. _While there are much easier ways to synchronize the color of MeshRenderer instances across clients, this is only for example purposes._ @@ -179,10 +179,10 @@ public class SpawnWithColorHandler : NetworkPrefabInstanceHandlerWithData public NetworkObject InstantiateSetDataAndSpawn(Vector3 position, Quaternion rotation, InstantiateData instantiateData) - { + { var instance = GetPrefabInstance(position, rotation, instantiateData); - // Set the spawndata before spawning + // Set the instantiate data before spawning m_NetworkManager.PrefabHandler.SetInstantiationData(instance, instantiateData); instance.GetComponent().Spawn(); @@ -199,10 +199,9 @@ public class SpawnWithColorHandler : NetworkPrefabInstanceHandlerWithData(); foreach (var renderer in meshRenderers) { - // Assign the color to each MeshRenderer (just a psuedo example) + // Assign the color to each MeshRenderer (just a pseudo example) renderer.material.color = instantiateData.Color; } - return instance.GetComponent(); } @@ -222,7 +221,6 @@ public class SpawnWithColorHandler : NetworkPrefabInstanceHandlerWithData Date: Wed, 13 Aug 2025 14:43:08 +0100 Subject: [PATCH 7/8] Docs review --- .../advanced-topics/network-prefab-handler.md | 81 ++++----- .../advanced-topics/object-pooling.md | 4 +- .../Documentation~/basics/object-spawning.md | 164 +++++++++--------- 3 files changed, 125 insertions(+), 124 deletions(-) diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md index 99ead5ffed..6608534375 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/network-prefab-handler.md @@ -1,32 +1,34 @@ # Network prefab handler -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. +The network prefab handler system provides advanced control over how network prefabs are instantiated and destroyed during runtime. You can use it to override the default Netcode for GameObjects [object spawning](../basics/object-spawning.md) behavior by implementing custom prefab handlers. The network prefab handler system is accessible from the [NetworkManager](../components/networkmanager.md) as `NetworkManager.PrefabHandler`. -## Overview +## When to use a prefab handler -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: +For an overview of the default object spawning behavior, refer to the [object spawning page](../basics/object-spawning.md). The default spawning behavior is designed to cover the majority of use cases, however there are some scenarios where you may need more control: - **Object pooling**: Reusing objects to reduce memory allocation and initialization costs. -- **Performance optimization**: Using different prefab variants on different platforms (e.g. using a simpler object for server simulation). +- **Performance optimization**: Using different prefab variants on different platforms (such as using a simpler object for server simulation). - **Custom initialization**: Setting up objects with game client specific data or configurations. - **Conditional spawning**: Initializing different prefab variants based on runtime conditions. -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. +The prefab handler system addresses these needs through an 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. -## Creating a prefab handler +## Create a prefab handler -Prefab handlers are classes which implement on of the Netcode for GameObjects prefab handler descriptions. There are currently two such descriptions: +Prefab handlers are classes that implement one of the Netcode for GameObjects prefab handler descriptions. There are currently two such descriptions: -- **INetworkPrefabInstanceHandler**: This is the simplest interface for custom prefab handlers. -- **NetworkPrefabInstanceHandlerWithData**: This specialized handler receives custom data from the authority during spawning, enabling dynamic prefab customization. +- [**INetworkPrefabInstanceHandler**](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.INetworkPrefabInstanceHandler.html): This is the simplest interface for custom prefab handlers. +- [**NetworkPrefabInstanceHandlerWithData**](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkPrefabInstanceHandlerWithData.html): This specialized handler receives custom data from the authority during spawning, enabling dynamic prefab customization. -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`). +When using a prefab handler, Netcode for GameObjects uses the `Instantiate` and `Destroy` methods instead of default spawn handlers for the NetworkObject during spawning and despawning. The authority instance uses 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`). -### INetworkPrefabInstanceHandler +### `INetworkPrefabInstanceHandler` -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. +For the simple use case of overriding a network prefab, implement the `INetworkPrefabInstanceHandler` interface and register an instance of that implementation with the `NetworkPrefabHandler` (`NetworkManager.PrefabHandler`). + +Use the `INetworkPrefabInstanceHandler` for situations where the prefab override behavior is consistent and known. ```csharp public interface INetworkPrefabInstanceHandler @@ -36,9 +38,11 @@ When all you want to do is handle overriding a network prefab, implementing the } ``` -### NetworkPrefabInstanceHandlerWithData +### `NetworkPrefabInstanceHandlerWithData` + +If you want to provide additional serialized data during the instantiation process, you can derive from the `NetworkPrefabInstanceHandlerWithData` class. -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). +The `NetworkPrefabInstanceHandlerWithData` class allows you to send custom data from the authority during object spawning. This extra data can then be used to change the behavior of the `Instantiate` method. Using `NetworkPrefabInstanceHandlerWithData`, you can send any custom type that is serializable using [`INetworkSerializable`](serialization/inetworkserializable.md). ```csharp public abstract class NetworkPrefabInstanceHandlerWithData : INetworkPrefabInstanceHandlerWithData @@ -49,9 +53,9 @@ public abstract class NetworkPrefabInstanceHandlerWithData : INetworkPrefabIn } ``` -## Prefab handler registration +## Register a prefab handler -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). +Once you've [created a prefab handler](#create-a-prefab-handler), whether by implementing or deriving, you need to register any new instance of that 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). ```csharp public class GameManager : NetworkBehaviour @@ -66,21 +70,21 @@ public class GameManager : NetworkBehaviour } ``` -In order to un-register a prefab handler, you can [invoke the `NetworkManager.PrefabHandler.RemoveHandler` method](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@2.4/api/Unity.Netcode.NetworkPrefabHandler.html#Unity_Netcode_NetworkPrefabHandler_RemoveHandler_System_UInt32_) (_There are several override versions of this method_). +To un-register a prefab handler, you can [invoke the `NetworkManager.PrefabHandler.RemoveHandler` method](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest?subfolder=/api/Unity.Netcode.NetworkPrefabHandler.html#Unity_Netcode_NetworkPrefabHandler_RemoveHandler_System_UInt32_). There are several override versions of this method. ## Object spawning with prefab handlers -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. +Once a prefab handler is registered, Netcode for GameObjects automatically uses 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. 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). ### Object spawning with custom data -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. +When using a handler derived from `NetworkPrefabInstanceHandlerWithData`, you must manually set the instantiation data after instantiating the instance (but before spawning) by invoking 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. -#### Simple Example +#### Examples -Below we can find a simple "pseudo" 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. +The first example here is a pseudo-script where the `InstantiateData` structure implements the `INetworkSerializable` interface and is used to serialize instantiation data for a network prefab defined within the `SpawnPrefabWithColor` NetworkBehaviour. ```csharp /// @@ -103,9 +107,9 @@ public struct InstantiateData : INetworkSerializable } ``` -The below `SpawnPrefabWithColor` is an example of a NetworkBehavior component, to be placed on an in-scene placed NetworkObject, that handles the instantiation of a prefab handler (`SpawnWithColorHandler`) and the means to configure the network prefab to register with the handler. It also has a `SpawnWithColorSystem.SpawnObject` method that can be used to instantiate an instance of the assigned network prefab instance that will also have instantiation data associated with it that contains the color to be applied to the instance's `MeshRenderer`s. +The second example, `SpawnPrefabWithColor`, is an example of a NetworkBehavior component, to be placed on an in-scene placed NetworkObject, that handles the instantiation of a prefab handler (`SpawnWithColorHandler`) and the means to configure the network prefab to register with the handler. It also has a `SpawnWithColorSystem.SpawnObject` method that can instantiate an instance of the assigned network prefab instance that will also have instantiation data associated with it that contains the color to be applied to the instance's MeshRenderers. -_While there are much easier ways to synchronize the color of MeshRenderer instances across clients, this is only for example purposes._ +There are easier ways to synchronize the color of MeshRenderer instances across clients, this is only for example purposes. ```csharp /// @@ -146,13 +150,13 @@ public class SpawnPrefabWithColor : NetworkBehaviour } } ``` -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. +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. -Below we will find the slightly more complex, of the three scripts, `SpawnWithColorHandler` with a constructor that automatically registers itself and the network prefab with the `NetworkManager.PrefabHandler`: +The third example is the most complex: `SpawnWithColorHandler` with a constructor that automatically registers itself and the network prefab with the `NetworkManager.PrefabHandler`: ```csharp /// -/// The prefan instance handler that uses instantiation data to handle updating +/// The prefab instance handler that uses instantiation data to handle updating /// the instance's s material's color. (example purposes only) /// public class SpawnWithColorHandler : NetworkPrefabInstanceHandlerWithData @@ -219,37 +223,36 @@ public class SpawnWithColorHandler : NetworkPrefabInstanceHandlerWithData [!NOTE] -> A caveat of the above two rules is when one of the children `GameObject`s also has a `NetworkObject` component assigned to it (a.k.a. "Nested NetworkObjects"). Because nested `NetworkObject` components aren't permited in network prefabs, Netcode for GameObjects will notify you in the editor if you are trying to add more than one `NetworkObject` to a Prefab and won't allow you to do this. +> A caveat of the above two rules is when one of the children `GameObject`s also has a `NetworkObject` component assigned to it (a.k.a. "Nested NetworkObjects"). Because nested `NetworkObject` components aren't permited in network prefabs, Netcode for GameObjects will notify you in the editor if you are trying to add more than one `NetworkObject` to a prefab and won't allow you to do this. When a `NetworkBehaviour` is assigned to a `NetworkObject`, the `NetworkObject.NetworkObjectId` is used to help determine which `NetworkBehaviour` component instance will receive an update to a `NetworkVariable` or where to invoke an RPC. A `NetworkObject` component can have one or more `NetworkBehaviour` components assigned to it. -### Registering a Network Prefab +### Registering a network prefab -You must register a Network Prefab instance with a `NetworkManager` using a `NetworkedPrefabsList` scriptable object. -There are four steps to registering a network Prefab with a `NetworkManager`: +You must register a Network prefab instance with a `NetworkManager` using a `NetworkedprefabsList` scriptable object. +There are four steps to registering a network prefab with a `NetworkManager`: -1. Create a Network Prefab by creating a Prefab with a `NetworkObject` component attached to the root `GameObject`. -2. Create a scriptable object called `NetworkedPrefabsList` by right-clicking the project window, then: `Create/Netcode/NetworkedPrefabsList`. -3. Add your Network Prefab to the `NetworkPrefabsList`. -4. Add the `NetworkPrefabsList` to the Network Prefabs Lists that's associated with a `NetworkManager`. +1. Create a Network prefab by creating a prefab with a `NetworkObject` component attached to the root `GameObject`. +2. Create a scriptable object called `NetworkedprefabsList` by right-clicking the project window, then: `Create/Netcode/NetworkedprefabsList`. +3. Add your Network prefab to the `NetworkprefabsList`. +4. Add the `NetworkprefabsList` to the Network prefabs Lists that's associated with a `NetworkManager`. -### Spawning a Network Prefab (Overview) +### Spawning a network prefab (overview) -When using a [server authoritative networking model](../terms-concepts/authority/#server-authority) only the server or host can spawn netcode objects. Under a [distributed authority networking model](..terms-concepts/authority/#distributed-authority), any game client can spawn netcode objects. The game client that spawned the network object then becomes the [authority](../terms-concepts/authority.md) of that object. +When using a [server authoritative networking model](../terms-concepts/authority.md#server-authority) only the server or host can spawn netcode objects. Under a [distributed authority networking model](../terms-concepts/authority.md#distributed-authority), any game client can spawn netcode objects. The game client that spawned the network object then becomes the [authority](../terms-concepts/authority.md) of that object. -To spawn a network prefab, you must first create an instance of the network Prefab and then invoke the spawn method on the `NetworkObject` component of the instance you created. -_In most cases, you will want to keep the `NetworkObject` component attached to the root `GameObject` of the network prefab._ +To spawn a network prefab, you must first create an instance of the network prefab and then invoke the spawn method on the NetworkObject component of the instance you created. In most cases, you will want to keep the NetworkObject component attached to the root GameObject of the network prefab. -By default a newly spawned network Prefab instance is owned by the authority unless otherwise specified. +By default, a newly spawned network prefab instance is owned by the authority unless otherwise specified. See [Ownership](networkobject.md#ownership) for more information. -The following is a basic example of how to spawn a network Prefab instance: +The following is a basic example of how to spawn a network prefab instance: ```csharp -var instance = Instantiate(myPrefab); +var instance = Instantiate(myprefab); var instanceNetworkObject = instance.GetComponent(); instanceNetworkObject.Spawn(); ``` @@ -56,30 +55,30 @@ When you set the destroyWithScene property to `false` it will be treated the sam > [!NOTE] > You might find it useful to add a `GameObject` property in a `NetworkBehaviour`-derived component to use when assigning a network prefab instance for dynamically spawning. You need to make sure to instantiate a new instance **prior** to spawning. If you attempt to just spawn the actual network prefab instance it can result in unexpected results. -### Taking Prefab Overrides Into Consideration +### Taking prefab overrides into consideration -Sometimes, you might want to use a different prefab instance on the authority compare to other clients. You should take this into consideration when dynamically spawning a network prefab. If you're running as a host, you want the override to spawn since a host is both a server and a client. However, if you also want to have the ability to run as a dedicated server, you might want to spawn the source network prefab. +Sometimes, you might want to use a different prefab instance on the authority compared to other clients. You should take this into consideration when dynamically spawning a network prefab. If you're running as a host, you want the override to spawn since a host is both a server and a client. However, if you also want to have the ability to run as a dedicated server, you might want to spawn the source network prefab. There are two ways you can accomplish this, as explained below. -#### Get The Network Prefab Override First +#### Get the network prefab override first This option provides you with the overall view of getting the network prefab override, instantiating it, and then spawning it. ```csharp -var instance = Instantiate(NetworkManager.GetNetworkPrefabOverride(myPrefab)); +var instance = Instantiate(NetworkManager.GetNetworkprefabOverride(myprefab)); var instanceNetworkObject = instance.GetComponent(); instanceNetworkObject.Spawn(); ``` -In the above script, we get the prefab override using the `NetworkManager.GetNetworkPrefabOverride` method. Then we create an instance of the network prefab override, and finally we spawn the network prefab override instance's `NetworkObject`. +In the above script, we get the prefab override using the `NetworkManager.GetNetworkprefabOverride` method. Then we create an instance of the network prefab override, and finally we spawn the network prefab override instance's `NetworkObject`. #### Using InstantiateAndSpawn The second option is to leverage the `NetworkSpawnManager.InstantiateAndSpawn` method that handles whether or not to spawn an override for you. The below script is written as if it's being invoked within a `NetworkBehaviour`. ```csharp -var networkObject = NetworkManager.SpawnManager.InstantiateAndSpawn(myPrefab, ownerId); +var networkObject = NetworkManager.SpawnManager.InstantiateAndSpawn(myprefab, ownerId); ``` We pass in the overridden source network prefab we want to have instantiated and spawned, and then it returns the instantiated and spawned `NetworkObject` of the spawned object. The default behavior of `InstantiateAndSpawn` is to spawn the original source prefab if running as a server and the override otherwise. @@ -87,20 +86,20 @@ We pass in the overridden source network prefab we want to have instantiated and `InstantiateAndSpawn` has several parameters to provide more control over this process: ```csharp -InstantiateAndSpawn(NetworkObject networkPrefab, ulong ownerClientId = NetworkManager.ServerClientId, bool destroyWithScene = false, bool isPlayerObject = false, bool forceOverride = false, Vector3 position = default, Quaternion rotation = default) +InstantiateAndSpawn(NetworkObject networkprefab, ulong ownerClientId = NetworkManager.ServerClientId, bool destroyWithScene = false, bool isPlayerObject = false, bool forceOverride = false, Vector3 position = default, Quaternion rotation = default) ``` Looking at the parameters, we can see it defaults to the server as the owner, ensures that the instantiated `NetworkObject` won't be destroyed if the scene is unloaded, is not spawned as a player, has a `forceOverride` parameter, and provides a way to set the position and rotation of the newly instantiated `NetworkObject`. The `forceOverride` parameter, when set to true, will always use the override. -To override prefabs on non-authority game clients, see the [Network prefab handler](./network-prefab-handler.md). +To override prefabs on non-authority game clients, refer to the [network prefab handler page](../advanced-topics/network-prefab-handler.md). -## Destroying / Despawning +## Destroying and despawning -By default, a spawned network Prefab instance that is destroyed on the authority will be automatically destroyed on all clients. +By default, a spawned network prefab instance that is destroyed on the authority will be automatically destroyed on all clients. -When a client disconnects, all network Prefab instances dynamically created during the network session will be destroyed on the client-side by default. If you don't want that to happen, set the `DontDestroyWithOwner` field on `NetworkObject` to true before despawning. +When a client disconnects, all network prefab instances dynamically created during the network session will be destroyed on the client-side by default. If you don't want that to happen, set the `DontDestroyWithOwner` field on `NetworkObject` to true before despawning. To do this at runtime: @@ -113,53 +112,52 @@ To make this the default from the editor Inspector view: ![image](../images/DontDestroyWithOwner.png) -As an alternative way, you can make the `NetworkObject.DontDestroyWithOwner` property default to `true` by setting it on the `NetworkObject` itself like in the above screenshot. +As an alternative, you can make the `NetworkObject.DontDestroyWithOwner` property default to `true` by setting it on the `NetworkObject` itself like in the above screenshot. ### Despawning -Only the authority can despawn a `NetworkObject`, and the default despawn behavior is to destroy the associated GameObject. to despawn but not destroy a `NetworkObject`, you should call `NetworkObject.Despawn` and pass false as the parameter. Non-authority clients will always be notified and will mirror the despawn behavior. If you despawn and destroy on the authority then all other connected clients will despawn and then destroy the `GameObject` that the `NetworkObjet` component is attached to. +Only the authority can despawn a NetworkObject, and the default despawn behavior is to destroy the associated GameObject. To despawn but not destroy a NetworkObject, call `NetworkObject.Despawn` and pass false as the parameter. Non-authority clients will always be notified and will mirror the despawn behavior. If you despawn and destroy on the authority then all other connected clients will despawn and then destroy the GameObject that the NetworkObject component is attached to. -On the non-authority side, you should never call `Object.Destroy` on any `GameObject` with a `NetworkObject` component attached to it (this isn't supported and will cause an exception to be thrown). To allow non-authority clients to destroy objects they do not own, have the relevant client invoke an RPC to defer the despawning on the authority side. +On the non-authority side, you should never call `Object.Destroy` on any GameObject with a NetworkObject component attached to it (this isn't supported and will cause an exception to be thrown). To allow non-authority clients to destroy objects they do not own, have the relevant client invoke an RPC to defer the despawning on the authority side. -The only way to despawn `NetworkObject` for a specific client is to use `NetworkObject.NetworkHide`. -See: [Object Visibility](object-visibility.md) for more information on this. +The only way to despawn a NetworkObject for a specific client is to use `NetworkObject.NetworkHide`. Refer to the [object visibility page](object-visibility.md) for more information. > [!NOTE] > If you have `GameObject` children, with `NetworkBehaviour` components attached, of a parent `GameObject`, with a `NetworkObject` component attached, you can't disable the `GameObject` children before spawning or despawning. Doing so, in v1.0.0, can cause unexpected results and it's recommended to make sure all children are enabled in the hierarchy before spawning or despawning. -## Dynamically Spawned Network Prefabs +## Dynamically spawned network prefabs -Netcode for GameObjects uses the term "dynamically spawned" to convey that the `NetworkObject` is being spawned via user specific code. Whereas a player or in-scene placed `NetworkObject` (with scene management enabled) is typically spawned by Netcode for GameObjects. There are several ways to spawn a network Prefab via code: +Netcode for GameObjects uses the term "dynamically spawned" to convey that the `NetworkObject` is being spawned via user specific code. Whereas a player or in-scene placed `NetworkObject` (with scene management enabled) is typically spawned by Netcode for GameObjects. There are several ways to spawn a network prefab via code: -### Dynamic Spawning (non-pooled): +### Dynamic spawning (non-pooled): -This type of dynamically spawned `NetworkObject` typically is a simple wrapper class that holds a reference to the Prefab asset. In the example below, the `NonPooledDynamicSpawner.PrefabToSpawn` property holds a reference to the network prefab: +This type of dynamically spawned `NetworkObject` typically is a simple wrapper class that holds a reference to the prefab asset. In the example below, the `NonPooledDynamicSpawner.prefabToSpawn` property holds a reference to the network prefab: ```csharp public class NonPooledDynamicSpawner : NetworkBehaviour { - public GameObject PrefabToSpawn; + public GameObject prefabToSpawn; public bool DestroyWithSpawner; - private GameObject m_PrefabInstance; + private GameObject m_prefabInstance; private NetworkObject m_SpawnedNetworkObject; public override void OnNetworkSpawn() { // Only the authority spawns, other clients will disable this component on their side enabled = HasAuthority; - if (!enabled || PrefabToSpawn == null) + if (!enabled || prefabToSpawn == null) { return; } // Instantiate the GameObject Instance - m_PrefabInstance = Instantiate(PrefabToSpawn); + m_prefabInstance = Instantiate(prefabToSpawn); // Optional, this example applies the spawner's position and rotation to the new instance - m_PrefabInstance.transform.position = transform.position; - m_PrefabInstance.transform.rotation = transform.rotation; + m_prefabInstance.transform.position = transform.position; + m_prefabInstance.transform.rotation = transform.rotation; // Get the instance's NetworkObject and Spawn - m_SpawnedNetworkObject = m_PrefabInstance.GetComponent(); + m_SpawnedNetworkObject = m_prefabInstance.GetComponent(); m_SpawnedNetworkObject.Spawn(); } @@ -180,36 +178,36 @@ Consumable and/or items that can be picked up by a player or NPC(that is, a weap > While the NonPooledDynamicSpawner example is one of the simplest ways to spawn a NetworkObject, there is a memory allocation cost associated with instantiating and destroying the GameObject and all attached components. This design pattern can sometimes be all you need for the netcode game asset you are working with, and other times you might want to respawn/re-use the object instance. When performance is a concern and you want to spawn more than just one `NetworkObject` during the lifetime of the spawner or want to repeatedly respawn a single `NetworkObject`, the less proccessor and memory allocation intensive technique is to use [pooled dynamic spawning](#pooled-dynamic-spawning). > [!NOTE] -> Really, when we use the term "non-pooled" more often than not we are referring to the concept that a `GameObject` will be instantiated on all game clients each time an instance is spawned. +> Generally, the term "non-pooled" refers to the concept that a GameObject will be instantiated on all game clients each time an instance is spawned. -### Pooled Dynamic Spawning +### Pooled dynamic spawning Pooled dynamic spawning is when netcode objects (`GameObject` with one `NetworkObject` component) aren't destroyed on game clients when despawned. Instead, specific components are just disabled (or the `GameObject` itself) when a netcode object is despawned. A pooled dynamically spawned netcode object is typically instantiated during an already memory allocation heavy period of time (like when a scene is loaded or even at the start of your application before even establishing a network connection). Pooled dynamically spawned netcode objects are more commonly thought of as more than one netcode object that can be re-used without incurring the memory allocation and initialization costs. However, you might also run into scenarios where you need just one dynamically spawned netcode object to be treated like a pooled dynmically spawned netcode object. -Fortunately, Netcode for GameObjects provides you with a way to be in control over the instatiation and destruction process for one or many netcode objects by via the `INetworkPrefabInstanceHandler` interface. Any `INetworkPrefabInstanceHandler`implementation should be registered with the `NetworkPrefabHandler`(for multiple netcode objects see [Object Pooling](../advanced-topics/object-pooling.md)) to accomplish this. +Fortunately, Netcode for GameObjects provides you with a way to be in control over the instatiation and destruction process for one or many netcode objects by via the `INetworkprefabInstanceHandler` interface. Any `INetworkprefabInstanceHandler`implementation should be registered with the `NetworkprefabHandler`(for multiple netcode objects see [Object Pooling](../advanced-topics/object-pooling.md)) to accomplish this. -The easiest way to not destroy a network Prefab instance is to have something, other than the instance itself, keeping a reference to the instance. This way you can simply set the root `GameObject` to be inactive when it's despawned while still being able to set it active when the same network Prefab type needs to be respawned. Below is one example of how you can accomplish this for a single netcode object instance: +The easiest way to not destroy a network prefab instance is to have something, other than the instance itself, keeping a reference to the instance. This way you can simply set the root `GameObject` to be inactive when it's despawned while still being able to set it active when the same network prefab type needs to be respawned. Below is one example of how you can accomplish this for a single netcode object instance: ```csharp -public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstanceHandler +public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkprefabInstanceHandler { - public GameObject PrefabToSpawn; - public bool SpawnPrefabAutomatically; + public GameObject prefabToSpawn; + public bool SpawnprefabAutomatically; - private GameObject m_PrefabInstance; + private GameObject m_prefabInstance; private NetworkObject m_SpawnedNetworkObject; private void Start() { // Instantiate our instance when we start (for all connected game clients) - m_PrefabInstance = Instantiate(PrefabToSpawn); + m_prefabInstance = Instantiate(prefabToSpawn); - // Get the NetworkObject component assigned to the Prefab instance - m_SpawnedNetworkObject = m_PrefabInstance.GetComponent(); + // Get the NetworkObject component assigned to the prefab instance + m_SpawnedNetworkObject = m_prefabInstance.GetComponent(); // Set it to be inactive - m_PrefabInstance.SetActive(false); + m_prefabInstance.SetActive(false); } private IEnumerator DespawnTimer() @@ -229,24 +227,24 @@ public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstan /// /// Invoked only on non-authority clients - /// INetworkPrefabInstanceHandler.Instantiate implementation + /// INetworkprefabInstanceHandler.Instantiate implementation /// Called when Netcode for GameObjects need an instance to be spawned /// public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation) { - m_PrefabInstance.SetActive(true); - m_PrefabInstance.transform.position = transform.position; - m_PrefabInstance.transform.rotation = transform.rotation; + m_prefabInstance.SetActive(true); + m_prefabInstance.transform.position = transform.position; + m_prefabInstance.transform.rotation = transform.rotation; return m_SpawnedNetworkObject; } /// /// Called on all game clients - /// INetworkPrefabInstanceHandler.Destroy implementation + /// INetworkprefabInstanceHandler.Destroy implementation /// public void Destroy(NetworkObject networkObject) { - m_PrefabInstance.SetActive(false); + m_prefabInstance.SetActive(false); } public void SpawnInstance() @@ -256,9 +254,9 @@ public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstan return; } - if (m_PrefabInstance != null && m_SpawnedNetworkObject != null && !m_SpawnedNetworkObject.IsSpawned) + if (m_prefabInstance != null && m_SpawnedNetworkObject != null && !m_SpawnedNetworkObject.IsSpawned) { - m_PrefabInstance.SetActive(true); + m_prefabInstance.SetActive(true); m_SpawnedNetworkObject.Spawn(); StartCoroutine(DespawnTimer()); } @@ -266,16 +264,16 @@ public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstan public override void OnNetworkSpawn() { - // We register our network Prefab and this NetworkBehaviour that implements the - // INetworkPrefabInstanceHandler interface with the Prefab handler - NetworkManager.PrefabHandler.AddHandler(PrefabToSpawn, this); + // We register our network prefab and this NetworkBehaviour that implements the + // INetworkprefabInstanceHandler interface with the prefab handler + NetworkManager.prefabHandler.AddHandler(prefabToSpawn, this); - if (!HasAuthority || !SpawnPrefabAutomatically) + if (!HasAuthority || !SpawnprefabAutomatically) { return; } - if (SpawnPrefabAutomatically) + if (SpawnprefabAutomatically) { SpawnInstance(); } @@ -293,30 +291,30 @@ public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstan public override void OnDestroy() { // This example destroys the - if (m_PrefabInstance != null) + if (m_prefabInstance != null) { // Always deregister the prefab - NetworkManager.Singleton.PrefabHandler.RemoveHandler(PrefabToSpawn); - Destroy(m_PrefabInstance); + NetworkManager.Singleton.prefabHandler.RemoveHandler(prefabToSpawn); + Destroy(m_prefabInstance); } base.OnDestroy(); } } ``` -You might run across a situation where you still want other components on the root `GameObject` of your network Prefab instance to remain active. Primarily, you want to be able to easily disable the components that would normally be active when the netcode object is considered spawned. +You might run across a situation where you still want other components on the root `GameObject` of your network prefab instance to remain active. Primarily, you want to be able to easily disable the components that would normally be active when the netcode object is considered spawned. -Below is an example of what a non-pooled friendly Prefab might look like: +Below is an example of what a non-pooled friendly prefab might look like: ![image](../images/non-pooled-friendly-prefab.png) -The issues you might run into with the above Prefab hierarchy is that everything is on a single `GameObject`, and as such if you wanted to disable the `MeshRenderer` and the `NetworkObjectLabel`, [one of our classes in the Netcode for GameObjects test project](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/blob/f0631414e5a5358a5ac7811d43273b1a82a60ca9/testproject/Assets/Scripts/NetworkObjectLabel.cs#L4), you would need to get those component types before disabling them (that is, during `Start` or `OnNetworkSpawn` or get them when `OnNetworkDespawn` is invoked). +The issues you might run into with the above prefab hierarchy is that everything is on a single `GameObject`, and as such if you wanted to disable the `MeshRenderer` and the `NetworkObjectLabel`, [one of our classes in the Netcode for GameObjects test project](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/blob/f0631414e5a5358a5ac7811d43273b1a82a60ca9/testproject/Assets/Scripts/NetworkObjectLabel.cs#L4), you would need to get those component types before disabling them (that is, during `Start` or `OnNetworkSpawn` or get them when `OnNetworkDespawn` is invoked). -To reduce this level of complexity, a more "pooled dynamic spawning" friendly Prefab heirarchy might look like this: +To reduce this level of complexity, a more "pooled dynamic spawning" friendly prefab heirarchy might look like this: ![image](../images/pooled-friendly-prefab.png) -The `NetworkObject` sits at the root `GameObject` of the network prefab. The child `GameObject`, SpawnedComponents, then has everything that you might want to have disabled when the network Prefab instance isn't spawned: +The `NetworkObject` sits at the root `GameObject` of the network prefab. The child `GameObject`, SpawnedComponents, then has everything that you might want to have disabled when the network prefab instance isn't spawned: ![image](../images/pooled-friendly-prefab-child.png) @@ -325,7 +323,7 @@ This reduces the complexity down to setting the SpawnedComponents `GameObject` t > [!NOTE] > Using this type of a hierarchical separation is useful in many ways (especially when you have a much more complex prefab). For more complex prefabs, you can further expand this pattern into specific categories (that is, visuals, physics, sound, etc) which will provide you with a more macrocosmic way to control enabling or disabling many different components without having to have references to all of them. -## In-Scene Placed `NetworkObject` +## In-scene placed `NetworkObject` Any objects in the scene with active and spawned `NetworkObject` components will get automatically replicated by Netcode. There is no need to manually spawn them when scene management is enabled in the `NetworkManager`. In-scene placed `NetworkObjects` should typically be used like a "static" netcode object, where the netcode object is typically spawned upon the scene being loaded on the authority-side and synchronized with other clients once they finish loading the same scene. @@ -334,14 +332,14 @@ Any objects in the scene with active and spawned `NetworkObject` components will Generally, there are **two** modes that define how an in-scene placed `NetworkObject` is synchronized. - Soft Synchronization (Scene Management enabled) -- Prefab Synchronization (Scene Management disabled) +- prefab Synchronization (Scene Management disabled) -### Soft Synchronization +### Soft synchronization `SoftSync` or "Soft Synchronization" is a term you might run across if you run into any issue with in-scene placed `NetworkObjects`. Soft synchronization only occurs if scene management is enabled in the `NetworkManager` properties. If you receive a "soft synchronization error", then this typically means that a client can't locate the same in-scene placed `NetworkObject` after loading a scene. -### Prefab Synchronization +### Prefab synchronization -`PrefabSync` or "Prefab Synchronization" is used if scene management is disabled in the `NetworkManager`. With Prefab synchronization, every in-scene placed `NetworkObject` has to be a network Prefab and must be registered with `NetworkPrefabs` list. When a client starts, Netcode will destroy all existing in-scene placed `NetworkObject`s and spawn its corresponding Prefab from the `NetworkPrefabs` list instead. This also means that you will have to implement your own scene manager and handle how you synchronize clients when they join a network session. +`prefabSync` or "prefab Synchronization" is used if scene management is disabled in the `NetworkManager`. With prefab synchronization, every in-scene placed `NetworkObject` has to be a network prefab and must be registered with `Networkprefabs` list. When a client starts, Netcode will destroy all existing in-scene placed `NetworkObject`s and spawn its corresponding prefab from the `Networkprefabs` list instead. This also means that you will have to implement your own scene manager and handle how you synchronize clients when they join a network session. -**PrefabSync is ONLY recommended for advanced development and/or multi project setups**. +**prefabSync is ONLY recommended for advanced development and/or multi project setups**. From 390f2c956b273f1d67529f8f354e7418edaba720 Mon Sep 17 00:00:00 2001 From: Amy Reeve Date: Wed, 13 Aug 2025 14:44:53 +0100 Subject: [PATCH 8/8] Adding new page to landing page --- com.unity.netcode.gameobjects/Documentation~/spawn-despawn.md | 1 + 1 file changed, 1 insertion(+) diff --git a/com.unity.netcode.gameobjects/Documentation~/spawn-despawn.md b/com.unity.netcode.gameobjects/Documentation~/spawn-despawn.md index fda545daff..506d7d5614 100644 --- a/com.unity.netcode.gameobjects/Documentation~/spawn-despawn.md +++ b/com.unity.netcode.gameobjects/Documentation~/spawn-despawn.md @@ -5,6 +5,7 @@ Spawn and despawn objects in your project. | **Topic** | **Description** | | :------------------------------ | :------------------------------- | | **[Object spawning](basics/object-spawning.md)** | Spawning in Netcode for GameObjects means to instantiate and/or spawn the object that is synchronized between all clients by the server. | +| **[Network prefab handler](advanced-topics/network-prefab-handler.md)** | The network prefab handler system provides advanced control over how network prefabs are instantiated and destroyed during runtime. | | **[Object pooling](advanced-topics/object-pooling.md)** | Netcode for GameObjects provides built-in support for Object Pooling, which allows you to override the default Netcode destroy and spawn handlers with your own logic. | | **[Object visibility](basics/object-visibility.md)** | Object (NetworkObject) visibility is a Netcode for GameObjects term used to describe whether a `NetworkObject` is visible to one or more clients as it pertains to a netcode/network perspective. | | **[Spawning synchronization](basics/spawning-synchronization.md)** | Ensuring that objects spawn in a synchronized manner across clients can be difficult, although the challenges differ depending on which [network topology](terms-concepts/network-topologies.md) you're using. | \ No newline at end of file