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 8eab667a68..cc095454ec 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 @@ -74,9 +74,9 @@ To un-register a prefab handler, you can [invoke the `NetworkManager.PrefabHandl ## Object spawning with prefab handlers -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. +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#spawn-a-network-prefab) 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). +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#consider-prefab-overrides). ### Object spawning with custom data @@ -255,4 +255,4 @@ When it comes to including instantiation data, you should be cautious about incl ## Additional resources - [Object pooling](./object-pooling.md) -- [Authority prefab overrides](../basics/object-spawning.md#taking-prefab-overrides-into-consideration) +- [Authority prefab overrides](../basics/object-spawning.md#consider-prefab-overrides) diff --git a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/networktime-ticks.md b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/networktime-ticks.md index 87d25f3b66..f33fa22926 100644 --- a/com.unity.netcode.gameobjects/Documentation~/advanced-topics/networktime-ticks.md +++ b/com.unity.netcode.gameobjects/Documentation~/advanced-topics/networktime-ticks.md @@ -1,13 +1,16 @@ -# NetworkTime and ticks +# Network time and ticks -## LocalTime and ServerTime +Understand how Netcode for GameObjects calculates network time, and when to use local time or server time. -Why are there two different time values and which one should be used? +## Local time and server time -Netcode for GameObjects (Netcode) uses a star topology. That means all communications happen between the clients and the server/host and never between clients directly. Messages take time to transmit over the network. That's why `RPCs` and `NetworkVariable` won't happen immediately on other machines. `NetworkTime` allows to use time while considering those transmission delays. +Netcode for GameObjects uses a star topology. That means all communications happen between the clients and the server or host, and never between clients directly. Messages take time to transmit over the network, so RPCs and `NetworkVariable` updates don't take effect immediately on other machines. Use `NetworkTime` to work with time while accounting for these transmission delays. -- `LocalTime` on a client is ahead of the server. If a server RPC is sent at `LocalTime` from a client it will roughly arrive at `ServerTime` on the server. -- `ServerTime` on clients is behind the server. If a client RPC is sent at `ServerTime` from the server to clients it will roughly arrive at `ServerTime` on the clients. +- `LocalTime` on a client is ahead of the server. It's the client's estimate of what the server clock reads right now: the last server time the client received, plus half the round trip time (RTT) to account for that message's own travel, plus a one-tick buffer. +- `ServerTime` on clients is behind the server. If the server sends a client RPC at `ServerTime`, the RPC arrives at roughly `ServerTime` on the clients. + +> [!NOTE] +> `LocalTime` leads the server clock by a fixed one tick, and that lead doesn't scale with latency. A message a client sends at `LocalTime` therefore reaches the server exactly as the server clock reaches the same value only when the RTT is about two ticks, which is roughly 67 ms at the default tick rate of 30. On faster connections the message arrives before that point, and on slower connections after it. Don't use `LocalTime` to predict which server tick processes a given message. For the measured latency in ticks, use `NetworkTimeSystem.TickLatency`, which is based on the full RTT. To give outgoing messages more lead, increase `NetworkTimeSystem.LocalBufferSec`, as described in [Configure the network time system](#configure-the-network-time-system). ```mermaid sequenceDiagram @@ -16,29 +19,31 @@ sequenceDiagram participant Receiver as Client ServerTime Note over Owner: Send message to server at LocalTime. Owner->>Server: Delay when sending message - Note over Server: Message arrives at ServerTime. + Note over Server: Message arrives near LocalTime, offset by half RTT minus one tick. Note over Server: On server: ServerTime == LocalTime. Note over Server: Send message to clients at LocalTime. Server->>Receiver: Delay when sending message Note over Receiver: Message arrives at ServerTime. ``` -`LocalTime` -- Use for player objects with client authority. -- Use if just a general time value is needed. +Use `LocalTime` in the following cases: + +- For player objects with client authority. +- For a general time value. + +Use `ServerTime` in the following cases: -`ServerTime`: -- For player objects with server authority (For example, by sending inputs to the server via RPCs) -- In sync with position updates of NetworkTransform for all `NetworkObjects` where the client isn't authoritative over the transform. -- For everything on non client controlled `NetworkObjects`. +- For player objects with server authority, for example by sending inputs to the server through RPCs. +- To stay in sync with position updates of the `NetworkTransform` component for all `NetworkObject` instances where the client isn't authoritative over the transform. +- For everything on `NetworkObject` instances that the client doesn't control. -## Examples +## Network time examples -### Example 1: Using network time to synchronize environments +### Synchronize environments with network time -Many games have environmental objects which move in a fixed pattern. By using network time these objects can be moved without having to synchronize their positions with a NetworkTransform. +Many games have environmental objects that move in a fixed pattern. Use network time to move these objects without synchronizing their positions with a `NetworkTransform` component. -For instance the following code can be used to create a moving elevator platform for a client authoritative game: +For example, the following code creates a moving elevator platform for a client-authoritative game: ```csharp using Unity.Netcode; @@ -55,9 +60,9 @@ public class MovingPlatform : MonoBehaviour } ``` -### Example 2: Using network time to create a synced event +### Create a synced event with network time -Most of the time aligning an effect precisely to time isn't needed. But in some cases for important effects or gameplay events it can help to improve consistency especially for clients with bad network connections. +You don't usually need to align an effect precisely to time. However, for important effects or gameplay events, precise alignment improves consistency, especially for clients with poor network connections. ```csharp using System.Collections; @@ -131,16 +136,17 @@ sequenceDiagram ``` > [!NOTE] -> Some components such as NetworkTransform add additional buffering. When trying to align an RPC event like in this example, an additional delay would need to be added. +> Some components, such as `NetworkTransform`, add additional buffering. When you align an RPC event as in this example, add an extra delay. -## Network Ticks +## Network ticks -Network ticks are run at a fixed rate. The 'Tick Rate' field on the NetworkManager can be used to set the tick rate. +Network ticks run at a fixed rate. To set the tick rate, use the **Tick Rate** field on the NetworkManager component. -What does changing the network tick affect? Changes to `NetworkVariables` aren't sent immediately. Instead during each network tick changes to `NetworkVariables` are collected and sent out to other peers. +Changing the network tick rate affects when Netcode for GameObjects sends `NetworkVariable` changes. It doesn't send them immediately. Instead, it collects the changes during each network tick and sends them to other peers. -To run custom code once per network tick (before `NetworkVariable` changes are collected) the `Tick` event on the `NetworkTickSystem` can be used. -```cs +To run custom code once per network tick, before Netcode for GameObjects collects `NetworkVariable` changes, subscribe to the `Tick` event on the `NetworkTickSystem`. + +```csharp public override void OnNetworkSpawn() { NetworkManager.NetworkTickSystem.Tick += Tick; @@ -158,13 +164,13 @@ public override void OnNetworkDespawn() // don't forget to unsubscribe ``` > [!NOTE] -> When using `FixedUpdate` or physics in your game, set the network tick rate to the same rate as the fixed update rate. The `FixedUpdate` rate can be changed in `Edit > Project Settings > Time Fixed Timestep`. +> When you use `FixedUpdate` or physics in your game, set the network tick rate to the same rate as the fixed update rate. To change the `FixedUpdate` rate, go to **Edit** > **Project Settings** > **Time** and set **Fixed Timestep**. -## Network FixedTime +## Network fixed time -`Network FixedTime` can be used to get a time value representing the time during a network tick. This works similar to `FixedUpdate` where `Time.fixedTime` represents the time during the `FixedUpdate`. +Use `FixedTime` to get a time value that represents the time during a network tick. This works in the same way as `FixedUpdate`, where `Time.fixedTime` represents the time during the `FixedUpdate`. -```cs +```csharp public void Update() { double time = NetworkManager.Singleton.LocalTime.Time; // time during this Update @@ -172,17 +178,23 @@ public void Update() } ``` -## NetworkTime Precision +## Network time precision + +Netcode for GameObjects calculates network time values as double-precision floating-point values. This keeps time accurate on long-running servers. If your game server runs sessions for a long time, such as multiple hours or days, don't convert this value to a float. Always use doubles for time-related calculations. -Network time values are calculated using double precisions. This allows time to stay accurate on long running servers. For game servers which run sessions for a long time (multiple hours or days) don't convert this value in a float and always use doubles for time related calculations. +For games with short play sessions, you can safely cast the time to a float or use `TimeAsFloat`. -For games with short play sessions casting the time to float is safe or `TimeAsFloat` can be used. +## Configure the network time system -## NetworkTimeSystem Configuration +To change how Netcode for GameObjects calculates network time, configure the `NetworkTimeSystem`. Refer to [`NetworkTimeSystem`](xref:Unity.Netcode.GameObjects.Timing.NetworkTimeSystem) for information about the properties you can modify. You can safely adjust all properties at runtime. For example, increase the buffer values for a client with a poor connection. > [!NOTE] -> The properties of the `NetworkTimeSystem` should be left untouched on the server/host. Changing the values on the client is sufficient to change the behavior of the time system. +> Don't change the properties of the `NetworkTimeSystem` on the server or host. To change the behavior of the time system, change the values on the client instead. -The way network time gets calculated can be configured in the `NetworkTimeSystem` if needed. Refer to the [API docs](xref:Unity.Netcode.GameObjects.Timing.NetworkTimeSystem) for information about the properties which can be modified. All properties can be safely adjusted at runtime. For instance, buffer values can be increased for a player with a bad connection. +## Additional resources - +- [`NetworkTimeSystem` API reference](xref:Unity.Netcode.GameObjects.Timing.NetworkTimeSystem) +- [NetworkManager](../components/core/networkmanager.md) +- [NetworkTransform](../components/helper/networktransform.md) +- [NetworkVariable](../basics/networkvariable.md) +- [Remote procedure calls (RPCs)](message-system/rpc.md) diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md b/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md index b72213f32e..df4c4410c3 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/object-spawning.md @@ -1,210 +1,224 @@ # 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 game clients. +Instantiate networked objects and synchronize them across all clients in a session. + +In Unity, you typically create a new GameObject using the `Instantiate` method, which only creates that object on the local machine. Spawning in Netcode for GameObjects means that you instantiate an object and Netcode for GameObjects synchronizes it across all clients. ## Network prefabs -A network prefab is any unity prefab asset that has one `NetworkObject` component attached to a `GameObject` within the prefab. More commonly, the `NetworkObject` component is attached to the root `GameObject` of the prefab asset because this allows any child `GameObject` to have `NetworkBehaviour` components automatically assigned to the `NetworkObject`. The reason for this is that a `NetworkObject` component attached to a `GameObject` will be assigned (associated with) any `NetworkBehaviour` components on: +A network prefab is any Unity prefab asset that has one `NetworkObject` component attached to a GameObject within the prefab. More commonly, the `NetworkObject` component is attached to the root GameObject of the prefab asset, because this allows any child GameObject to have `NetworkBehaviour` components automatically assigned to the `NetworkObject`. Netcode for GameObjects associates a `NetworkObject` component with any `NetworkBehaviour` components on: -- the same `GameObject` that the `NetworkObject` component is attached to -- any child or children of the `GameObject` that the `NetworkObject` is attached to. +- The same GameObject that the `NetworkObject` component is attached to. +- Any child GameObject of the GameObject that the `NetworkObject` is attached to. > [!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 these two rules is when one of the child GameObjects also has a `NetworkObject` component assigned to it, also known as nested NetworkObjects. Because nested `NetworkObject` components aren't permitted in network prefabs, Netcode for GameObjects notifies you in the Editor if you try to add more than one `NetworkObject` to a prefab, and doesn't allow it. + +When a `NetworkBehaviour` is assigned to a `NetworkObject`, Netcode for GameObjects uses the `NetworkObject.NetworkObjectId` to determine which `NetworkBehaviour` component instance receives an update to a `NetworkVariable`, or where to invoke an RPC. A `NetworkObject` component can have one or more `NetworkBehaviour` components assigned to it. + +### Register a network prefab -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. +You must register a network prefab instance with a `NetworkManager` using a `NetworkPrefabsList` scriptable object. -### Registering a network prefab +To register a network prefab with a `NetworkManager`, follow these steps: -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 prefab, then attach a `NetworkObject` component to its root GameObject. +1. In the **Project** window, right-click and select **Create** > **Netcode** > **Network Prefabs List**. +1. Add your network prefab to the `NetworkPrefabsList`. +1. In the **NetworkManager** component, add the `NetworkPrefabsList` to the **Network Prefabs Lists** property. -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`. +The network prefab is now registered, and you can spawn it at runtime. -### Spawning a network prefab (overview) +## Spawn a network prefab -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. +When you use a [server-authoritative networking model](../terms-concepts/authority.md#server-authority), only the server or host can spawn NetworkObjects. Under a [distributed authority networking model](../terms-concepts/authority.md#distributed-authority), any client can spawn NetworkObjects. The client that spawns the NetworkObject 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, first create an instance of the network prefab, then invoke the spawn method on the `NetworkObject` component of the instance you created. In most cases, keep the `NetworkObject` component attached to the root GameObject of the network prefab. -Refer to [NetworkObject ownership](../components/core/networkobject-ownership.md) for more information. +For more information, refer to [NetworkObject ownership](../components/core/networkobject-ownership.md). 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(); ``` -The `NetworkObject.Spawn` method takes 1 optional parameter that defaults to `true`: +The `NetworkObject.Spawn` method takes one optional parameter that defaults to `false`: ```csharp -public void Spawn(bool destroyWithScene = true); +public void Spawn(bool destroyWithScene = false); ``` -When you set the destroyWithScene property to `false` it will be treated the same as when you set [Object.DontDestroyOnLoad](https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html). Typically, you use this if you are loading a scene using [LoadSceneMode.Single](https://docs.unity3d.com/ScriptReference/SceneManagement.LoadSceneMode.html) parameter. +When `destroyWithScene` is `false`, the spawned instance behaves the same as an object you pass to [`Object.DontDestroyOnLoad`](https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html): unloading its scene doesn't destroy it. This is usually the behavior you want when you load scenes with the [`LoadSceneMode.Single`](https://docs.unity3d.com/ScriptReference/SceneManagement.LoadSceneMode.html) parameter. Set it to `true` if you instead want Unity to destroy the instance with its scene. -[Learn more about Netcode Scene Management here](scenemanagement/scene-management-overview.md) +For more information, refer to [Scene management overview](scenemanagement/scene-management-overview.md). > [!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. +> You might find it useful to add a GameObject property in a `NetworkBehaviour`-derived component to use when you assign a network prefab instance for dynamic spawning. Make sure you instantiate a new instance before you spawn it. If you spawn the network prefab asset itself, you get unexpected results. -### Taking prefab overrides into consideration +## Consider prefab overrides -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. +Sometimes, you might want to use a different prefab instance on the authority compared to other clients. Take this into account when you dynamically spawn a network prefab. If you run as a host, you want the override to spawn, because a host is both a server and a client. However, if you also want 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. +You can do this in two ways. -#### 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`. +The preceding script gets the prefab override with the `NetworkManager.GetNetworkPrefabOverride` method, creates an instance of the network prefab override, and then spawns that instance's `NetworkObject`. -#### Using InstantiateAndSpawn +### Instantiate and spawn in one call -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`. +The second option is to use the `NetworkSpawnManager.InstantiateAndSpawn` method, which handles whether to spawn an override for you. The following example assumes that you invoke it inside a `NetworkBehaviour`. ```csharp -var networkObject = NetworkManager.SpawnManager.InstantiateAndSpawn(myprefab, ownerId); +// SpawnManager.InstantiateAndSpawn takes the NetworkObject of the source prefab +var networkObject = NetworkManager.SpawnManager.InstantiateAndSpawn(myPrefab.GetComponent(), 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. +Pass in the source network prefab to instantiate and spawn. The method returns the `NetworkObject` of the spawned instance. By default, `InstantiateAndSpawn` spawns the original source prefab if you run as a server, and the override otherwise. `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`. +> [!NOTE] +> The first parameter is a `NetworkObject`, not a `GameObject`. If you only hold a reference to the prefab's `GameObject`, either get its `NetworkObject` component as shown in the previous example, or use the static `NetworkObject.InstantiateAndSpawn(GameObject networkPrefab, NetworkManager networkManager, ...)` overload, which accepts the `GameObject` directly. + +By default, these parameters set the server as the owner, keep the instantiated `NetworkObject` when Unity unloads the scene, don't spawn the object as a player object, don't force the prefab override, and set the position and rotation of the newly instantiated `NetworkObject`. -The `forceOverride` parameter, when set to true, will always use the override. +If you set `forceOverride` to `true`, Netcode for GameObjects always uses the override. -To override prefabs on non-authority game clients, refer to the [network prefab handler page](../advanced-topics/network-prefab-handler.md). +To override prefabs on non-authority clients, refer to [Network prefab handler](../advanced-topics/network-prefab-handler.md). -## Destroying and despawning +## Destroy and despawn objects -By default, a spawned network prefab instance that is destroyed on the authority will be automatically destroyed on all clients. +By default, when you destroy a spawned network prefab instance on the authority, Netcode for GameObjects automatically destroys it 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, the authority despawns and destroys every spawned `NetworkObject` that the disconnecting client owns, regardless of which client spawned it. This matters under a server-authoritative model, where clients can't spawn objects but commonly own objects that the server spawned. To keep an object alive after its owner disconnects, set the `DontDestroyWithOwner` field on `NetworkObject` to `true` while the object is still spawned. The authority then transfers or removes ownership instead of destroying the object. To do this at runtime: ```csharp m_SpawnedNetworkObject.DontDestroyWithOwner = true; -m_SpawnedNetworkObject.Despawn(); ``` -To make this the default from the editor Inspector view: +To make this the default in the **Inspector** window: -![image](../images/DontDestroyWithOwner.png) +![The Dont Destroy With Owner property enabled on the NetworkObject component of a prefab in the Inspector window.](../images/DontDestroyWithOwner.png) -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. +As an alternative, you can make the `NetworkObject.DontDestroyWithOwner` property default to `true` by setting it on the `NetworkObject` itself, as shown in the previous screenshot. -### Despawning +### Despawn an object -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. +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. Netcode for GameObjects always notifies non-authority clients, which mirror the despawn behavior. If you despawn and destroy on the authority, all other connected clients 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, never call `Object.Destroy` on any GameObject with a `NetworkObject` component attached to it. Netcode for GameObjects doesn't support this and throws an exception. To allow non-authority clients to destroy objects they don't own, have the relevant client invoke an RPC to defer the despawning on the authority side. -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. +The only way to despawn a `NetworkObject` for a specific client is to use `NetworkObject.NetworkHide`. For more information, refer to [Object visibility](object-visibility.md). > [!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. +> Don't disable child GameObjects that have `NetworkBehaviour` components attached before you spawn the parent `NetworkObject`. Netcode for GameObjects excludes disabled `NetworkBehaviour` components from spawning and synchronization, so make sure all child GameObjects are enabled in the hierarchy before you spawn. This restriction doesn't apply to despawning. -## Dynamically spawned network prefabs +## Spawn network prefabs dynamically -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 your own code spawns the `NetworkObject`, whereas Netcode for GameObjects typically spawns a player or in-scene placed `NetworkObject` when scene management is enabled. There are several ways to spawn a network prefab in code: -### Dynamic spawning (non-pooled): +### Spawn dynamically without pooling -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` is typically a simple wrapper class that holds a reference to the prefab asset. In the following example, the `NonPooledDynamicSpawner.PrefabToSpawn` property holds a reference to the network prefab: ```csharp - public class NonPooledDynamicSpawner : NetworkBehaviour - { - public GameObject prefabToSpawn; - public bool DestroyWithSpawner; - private GameObject m_prefabInstance; - private NetworkObject m_SpawnedNetworkObject; +using Unity.Netcode; +using UnityEngine; + +public class NonPooledDynamicSpawner : NetworkBehaviour +{ + public GameObject PrefabToSpawn; + public bool DestroyWithSpawner; + private GameObject m_PrefabInstance; + private NetworkObject m_SpawnedNetworkObject; - public override void OnNetworkSpawn() + public override void OnNetworkSpawn() + { + // Only the authority spawns, other clients will disable this component on their side + enabled = HasAuthority; + if (!enabled || PrefabToSpawn == null) { - // Only the authority spawns, other clients will disable this component on their side - enabled = HasAuthority; - if (!enabled || prefabToSpawn == null) - { - return; - } - // Instantiate the GameObject Instance - m_prefabInstance = Instantiate(prefabToSpawn); + return; + } + // Instantiate the GameObject Instance + m_PrefabInstance = Instantiate(PrefabToSpawn); - // Optional, this example applies the spawner's position and rotation to the new instance - m_prefabInstance.transform.SetPositionAndRotation(transform.position, transform.rotation); + // Optional, this example applies the spawner's position and rotation to the new instance + m_PrefabInstance.transform.SetPositionAndRotation(transform.position, transform.rotation); - // Get the instance's NetworkObject and Spawn - m_SpawnedNetworkObject = m_prefabInstance.GetComponent(); - m_SpawnedNetworkObject.Spawn(); - } + // Get the instance's NetworkObject and Spawn + m_SpawnedNetworkObject = m_PrefabInstance.GetComponent(); + m_SpawnedNetworkObject.Spawn(); + } - public override void OnNetworkDespawn() + public override void OnNetworkDespawn() + { + if (HasAuthority && DestroyWithSpawner && m_SpawnedNetworkObject != null && m_SpawnedNetworkObject.IsSpawned) { - if (HasAuthority && DestroyWithSpawner && m_SpawnedNetworkObject != null && m_SpawnedNetworkObject.IsSpawned) - { - m_SpawnedNetworkObject.Despawn(); - } - base.OnNetworkDespawn(); + m_SpawnedNetworkObject.Despawn(); } + base.OnNetworkDespawn(); } +} ``` -Consumable and/or items that can be picked up by a player or NPC(that is, a weapon, health, potion, etc.) would be some examples of when you might want to use non-pooled dynamically spawned `NetworkObjects`. +Consumables and items that a player or non-player character (NPC) can pick up, such as a weapon, health, or a potion, are examples of when to use non-pooled dynamically spawned `NetworkObject` instances. > [!NOTE] -> 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). +> The `NonPooledDynamicSpawner` example is one way to spawn a `NetworkObject`, but there's 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 asset you're working with, and other times you might want to respawn or reuse the object instance. When performance is a concern and you want to spawn more than one `NetworkObject` during the lifetime of the spawner, or want to repeatedly respawn a single `NetworkObject`, the less processor-intensive and memory-intensive technique is to [spawn dynamically with pooling](#spawn-dynamically-with-pooling). > [!NOTE] -> 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. +> Generally, the term non-pooled means that Netcode for GameObjects instantiates a GameObject on all clients each time you spawn an instance. -### Pooled dynamic spawning +### Spawn dynamically with pooling -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. +Pooled dynamic spawning is when clients don't destroy netcode objects (a GameObject with one `NetworkObject` component) when you despawn them. Instead, Netcode for GameObjects disables specific components, or the GameObject itself, when you despawn a netcode object. You typically instantiate a pooled dynamically spawned netcode object during a memory-allocation-heavy period, such as when Unity loads a scene, or at the start of your application before you establish a network connection. Pooled dynamically spawned netcode objects are usually multiple objects that you can reuse without incurring the memory allocation and initialization costs. However, you might also encounter scenarios where you need one dynamically spawned netcode object to behave like a pooled dynamically 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. +Netcode for GameObjects lets you control the instantiation and destruction process for one or many netcode objects via the `INetworkPrefabInstanceHandler` interface. To do this, register your `INetworkPrefabInstanceHandler` implementation with the `NetworkPrefabHandler`. For multiple netcode objects, refer to [Object pooling](../advanced-topics/object-pooling.md). -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: +One way to avoid destroying a network prefab instance is to have something other than the instance itself keep a reference to it. This way, you can set the root GameObject to inactive when you despawn it, and set it active again when you respawn the same network prefab type. The following example shows how to do this for a single netcode object instance: ```csharp -public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkprefabInstanceHandler +using System.Collections; +using Unity.Netcode; +using UnityEngine; + +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(); + m_SpawnedNetworkObject = m_PrefabInstance.GetComponent(); // Set it to be inactive - m_prefabInstance.SetActive(false); + m_PrefabInstance.SetActive(false); } private IEnumerator DespawnTimer() @@ -224,23 +238,26 @@ public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkprefabInstan /// /// Invoked only on non-authority clients - /// INetworkprefabInstanceHandler.Instantiate implementation - /// Called when Netcode for GameObjects need an instance to be spawned + /// INetworkPrefabInstanceHandler.Instantiate implementation + /// Called when Netcode for GameObjects needs an instance to be spawned /// public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation) { - m_prefabInstance.SetActive(true); - m_prefabInstance.transform.position.SetPositionAndRotation(transform.position, transform.rotation); + m_PrefabInstance.SetActive(true); + + // Use the position and rotation passed in by Netcode for GameObjects so that + // this instance matches the one on the authority + m_PrefabInstance.transform.SetPositionAndRotation(position, 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() @@ -250,9 +267,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()); } @@ -261,18 +278,15 @@ 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); + // INetworkPrefabInstanceHandler interface with the prefab handler + NetworkManager.PrefabHandler.AddHandler(PrefabToSpawn, this); - if (!HasAuthority || !SpawnprefabAutomatically) + if (!HasAuthority || !SpawnPrefabAutomatically) { return; } - if (SpawnprefabAutomatically) - { - SpawnInstance(); - } + SpawnInstance(); } public override void OnNetworkDespawn() @@ -286,56 +300,69 @@ 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); + // Always deregister the prefab. The NetworkManager can already be destroyed + // during teardown, so check it before using it to avoid a null reference. + if (NetworkManager != null) + { + NetworkManager.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 encounter a situation where you still want other components on the root GameObject of your network prefab instance to remain active. Primarily, you need to disable the components that are normally active when the netcode object is spawned. -Below is an example of what a non-pooled friendly prefab might look like: +The following image shows a prefab that's not pooling-friendly: -![image](../images/non-pooled-friendly-prefab.png) +![The Inspector window for the NotPooledFriendlyPrefab prefab, with the Transform, Mesh Renderer, Mesh Filter, NetworkObject, and Network Object Label components all attached to a single root GameObject.](../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 issue with the previous prefab hierarchy is that everything is on a single GameObject. If you want to disable the `MeshRenderer` and the `NetworkObjectLabel`, a class in the Netcode for GameObjects test project (refer to [`NetworkObjectLabel`](https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/blob/f0631414e5a5358a5ac7811d43273b1a82a60ca9/testproject/Assets/Scripts/NetworkObjectLabel.cs#L4) on GitHub), you need to get those component types before you disable them, for example during `Start` or `OnNetworkSpawn`, or when Netcode for GameObjects invokes `OnNetworkDespawn`. -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 pooling-friendly prefab hierarchy might look like this: -![image](../images/pooled-friendly-prefab.png) +![The Hierarchy window showing the PooledFriendlyPrefab root GameObject with a SpawnedComponents child, and the Inspector window showing only the Transform and NetworkObject components on the root.](../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`, contains everything you might want to disable when the network prefab instance isn't spawned: -![image](../images/pooled-friendly-prefab-child.png) +![The Inspector window for the SpawnedComponents child GameObject, with the Network Object Label, Mesh Renderer, and Mesh Filter components attached to it.](../images/pooled-friendly-prefab-child.png) -This reduces the complexity down to setting the SpawnedComponents `GameObject` to inactive, which will also disable all of the components attached to it. +This reduces the complexity to setting the `SpawnedComponents` GameObject to inactive, which also disables all the components attached to it. > [!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. +> This type of hierarchical separation is useful in many ways, especially when you have a much more complex prefab. For more complex prefabs, you can expand this pattern into specific categories, for example visuals, physics, and sound, which gives you a broader way to control enabling or disabling many different components without having to hold references to all of them. -## In-scene placed `NetworkObject` +## Use in-scene placed NetworkObjects -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. +Netcode for GameObjects automatically replicates any objects in the scene that have active and spawned `NetworkObject` components. There's no need to manually spawn them when scene management is enabled in the `NetworkManager`. Typically, use in-scene placed `NetworkObject` instances as static netcode objects: the authority spawns them when it loads the scene, and other clients synchronize them after they finish loading the same scene. -[Learn more about In-Scene Placed `NetworkObjects`](scenemanagement/inscene-placed-networkobjects.md) +For more information, refer to [In-scene placed NetworkObjects](scenemanagement/inscene-placed-networkobjects.md). -Generally, there are **two** modes that define how an in-scene placed `NetworkObject` is synchronized. +Two modes define how Netcode for GameObjects synchronizes an in-scene placed `NetworkObject`: -- Soft Synchronization (Scene Management enabled) -- prefab Synchronization (Scene Management disabled) +- Soft synchronization (scene management enabled) +- Prefab synchronization (scene management disabled) ### 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. +`SoftSync`, or soft synchronization, is a term you might encounter if you have an issue with in-scene placed `NetworkObject` instances. Soft synchronization only occurs if scene management is enabled in the `NetworkManager` properties. If you receive a soft synchronization error, this typically means that a client can't locate the same in-scene placed `NetworkObject` after it loads a scene. ### 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. +Netcode for GameObjects uses `PrefabSync`, or prefab synchronization, if scene management is disabled in the `NetworkManager`. With prefab synchronization, you must make every in-scene placed `NetworkObject` a network prefab and register it in the `NetworkPrefabs` list. When a client starts, Netcode for GameObjects destroys all existing in-scene placed `NetworkObject` instances and spawns their corresponding prefabs from the `NetworkPrefabs` list instead. This also means you must implement your own scene manager and handle how you synchronize clients when they join a network session. + +Only use `PrefabSync` for advanced development or multiproject setups, because it requires you to manage scene synchronization yourself. + +## Additional resources -**prefabSync is ONLY recommended for advanced development and/or multi project setups**. +- [NetworkObject ownership](../components/core/networkobject-ownership.md) +- [Authority](../terms-concepts/authority.md) +- [Scene management overview](scenemanagement/scene-management-overview.md) +- [In-scene placed NetworkObjects](scenemanagement/inscene-placed-networkobjects.md) +- [Network prefab handler](../advanced-topics/network-prefab-handler.md) +- [Object pooling](../advanced-topics/object-pooling.md) +- [Object visibility](object-visibility.md) diff --git a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/inscene-placed-networkobjects.md b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/inscene-placed-networkobjects.md index 617a8f5f36..c294056e87 100644 --- a/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/inscene-placed-networkobjects.md +++ b/com.unity.netcode.gameobjects/Documentation~/basics/scenemanagement/inscene-placed-networkobjects.md @@ -109,7 +109,7 @@ Using this approach allows you to: 1. Re-use the same single-spawn manager with any other network prefab registered with a `NetworkPrefabsList`. 2. Not worry about the complexities involved with treating an in-scene placed NetworkObject like a dynamically spawned one. -[You can see a hybrid approach example here.](../object-spawning.md#dynamic-spawning-non-pooled) +[You can see a hybrid approach example here.](../object-spawning.md#spawn-dynamically-without-pooling) ## Spawning and despawning in-scene placed NetworkObjects @@ -237,7 +237,7 @@ Referring back to the [section on complex in-scene NetworkObjects](#complex-in-s - Have another in-scene placed NetworkObject track which in-scene placed NetworkObjects have been destroyed and upon a player late-joining (that is, `OnClientConnected`) you would need to send the newly-joined client the list of in-scene placed NetworkObjects that it should destroy. This adds an additional in-scene placed NetworkObject to your scene hierarchy and will consume memory keeping track of what was destroyed. - Disable the visual and physics-related components (in Editor as a default) of the in-scene placed NetworkObject(s) in question and only enable them in `OnNetworkSpawn`. This doesn't delete/remove the in-scene placed NetworkObject(s) for the late-joining client and can be tricky to implement without running into edge case scenario bugs. -These two alternatives aren't recommended, but are worth briefly exploring to better understand why it's recommend to use a [non-pooled hybrid approach](../object-spawning.md#dynamic-spawning-non-pooled), or just not destroying the in-scene placed NetworkObject when despawning it. +These two alternatives aren't recommended, but are worth briefly exploring to better understand why it's recommend to use a [non-pooled hybrid approach](../object-spawning.md#spawn-dynamically-without-pooling), or just not destroying the in-scene placed NetworkObject when despawning it. ## Parenting in-scene placed NetworkObjects diff --git a/com.unity.netcode.gameobjects/Documentation~/components/core/playerobjects.md b/com.unity.netcode.gameobjects/Documentation~/components/core/playerobjects.md index a679806c44..5df98a5fd9 100644 --- a/com.unity.netcode.gameobjects/Documentation~/components/core/playerobjects.md +++ b/com.unity.netcode.gameobjects/Documentation~/components/core/playerobjects.md @@ -45,7 +45,7 @@ GetComponent().SpawnAsPlayerObject(clientId); If the player already had a prefab instance assigned, then the client owns the NetworkObject of that prefab instance unless there's additional server-side specific user code that removes or changes the ownership. -Alternatively, you can choose not to spawn anything immediately after a client connects and instead use a [NetworkBehaviour component](networkbehaviour.md) to handle avatar/initial player prefab selection. This NetworkBehaviour component could be configured by the server or initiating session owner, or be associated with an [in-scene](../../basics/scenemanagement/inscene-placed-networkobjects.md) or [dynamically spawned](../../basics/object-spawning.md#dynamically-spawned-network-prefabs) NetworkObject, as suits the needs of your project. +Alternatively, you can choose not to spawn anything immediately after a client connects and instead use a [NetworkBehaviour component](networkbehaviour.md) to handle avatar/initial player prefab selection. This NetworkBehaviour component could be configured by the server or initiating session owner, or be associated with an [in-scene](../../basics/scenemanagement/inscene-placed-networkobjects.md) or [dynamically spawned](../../basics/object-spawning.md#spawn-network-prefabs-dynamically) NetworkObject, as suits the needs of your project. ### Client-server contexts only @@ -55,7 +55,7 @@ In addition to the [session-mode agnostic spawning methods](#session-mode-agnost In addition to the [session-mode agnostic spawning methods](#session-mode-agnostic-methods) above, you can use the [`OnFetchLocalPlayerPrefabToSpawn`](xref:Unity.Netcode.NetworkManager.OnFetchLocalPlayerPrefabToSpawn) method to assign a unique player prefab on a per-client basis when in [distributed authority contexts](../../terms-concepts/distributed-authority.md). -To use `OnFetchLocalPlayerPrefabToSpawn` in your project, assign a callback handler to `OnFetchLocalPlayerPrefabToSpawn` and whatever the client script returns is what will be spawned for that client. Ensure that the prefab being spawned is in a NetworkPrefabList [registered with the NetworkManager](../../basics/object-spawning.md#registering-a-network-prefab). +To use `OnFetchLocalPlayerPrefabToSpawn` in your project, assign a callback handler to `OnFetchLocalPlayerPrefabToSpawn` and whatever the client script returns is what will be spawned for that client. Ensure that the prefab being spawned is in a NetworkPrefabList [registered with the NetworkManager](../../basics/object-spawning.md#register-a-network-prefab). If you don't assign a callback handler to `OnFetchLocalPlayerPrefabToSpawn`, then the default behavior is to return the `NetworkConfig.PlayerPrefab` (or null if neither are set). diff --git a/com.unity.netcode.gameobjects/Runtime/Timing/NetworkTimeSystem.cs b/com.unity.netcode.gameobjects/Runtime/Timing/NetworkTimeSystem.cs index 9432ccda98..f2bac8bb23 100644 --- a/com.unity.netcode.gameobjects/Runtime/Timing/NetworkTimeSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Timing/NetworkTimeSystem.cs @@ -7,10 +7,17 @@ namespace Unity.Netcode.GameObjects.Timing { /// /// is a standalone system which can be used to run a network time simulation. - /// The network time system maintains both a local and a server time. The local time is based on the server time - /// as last received from the server plus an offset based on the current RTT - in other words, it is a best-guess - /// effort at predicting what the server tick will be when a given network action is processed on the server. + /// The network time system maintains both a local and a server time. The local time is the server time as last + /// received from the server, plus half the current RTT to account for the delivery delay of that value, plus + /// - in other words, it is a best-guess estimate of what the server clock reads + /// right now. /// + /// + /// is not a prediction of the server tick that will process a message sent from this + /// client. Its lead over the server clock is , which does not scale with latency, + /// so a message sent at arrives before or after that tick depending on the RTT. For the + /// measured client-to-server latency in ticks, use , which is based on the full RTT. + /// [Serializable] [MovedFrom(true, "Unity.Netcode", null, null)] public class NetworkTimeSystem