From 243319c5115f37ef833b3787ab071435b7f09787 Mon Sep 17 00:00:00 2001 From: Christopher Pope Date: Thu, 6 Oct 2022 16:22:11 +0100 Subject: [PATCH 1/5] Merging Develop with Main (#813) * Updating docs page in 2D space shooter (#810) Updating docs page in 2D space shooter which has a reference to an unconfirmed feature (Prediction) * updating boss room example used in mid-game reconnecting doc (#725) Co-authored-by: Sara [Unity] <89528471+s-omeilia-unity@users.noreply.github.com> Co-authored-by: Christopher Pope * Update getting-started-boss-room.md (#812) Co-authored-by: LPLafontaineB Co-authored-by: Sara [Unity] <89528471+s-omeilia-unity@users.noreply.github.com> Co-authored-by: Sam Bellomo <71790295+SamuelBellomo@users.noreply.github.com> --- docs/advanced-topics/reconnecting-mid-game.md | 4 +- docs/learn/bitesize/bitesize-spaceshooter.md | 2 +- .../bossroom/getting-started-boss-room.md | 40 +++++++------------ 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/docs/advanced-topics/reconnecting-mid-game.md b/docs/advanced-topics/reconnecting-mid-game.md index 8b8518854..783df14cc 100644 --- a/docs/advanced-topics/reconnecting-mid-game.md +++ b/docs/advanced-topics/reconnecting-mid-game.md @@ -46,9 +46,9 @@ Depending on your game, you may want to add the following features as well: Check out our [Boss Room sample](../learn/bossroom/getting-started-boss-room.md) for an example implementation of automatic reconnection. -The entry point for this feature is in [this class](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Assets/Scripts/Gameplay/ConnectionManagement/ClientGameNetPortal.cs). Boss Room's implementation uses a coroutine (`TryToReconnect`) that attempts to reconnect a few times sequentially, until it either succeeds or surpasses the defined maximum number of attempts. +The entry point for this feature is in [this class](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Assets/Scripts/ConnectionManagement/ConnectionState/ClientReconnectingState.cs). Boss Room's implementation uses a state inside a state machine that starts a coroutine on entering it (`ReconnectCoroutine`) that attempts to reconnect a few times sequentially, until it either succeeds, surpasses the defined maximum number of attempts, or is cancelled. (See `OnClientConnected`, `OnClientDisconnect`, `OnDisconnectReasonReceived`, and `OnUserRequestedShutdown`) -The coroutine is triggered when a client disconnects, depending on the reason of that disconnect (see `OnDisconnectOrTimeout`) and is stopped when a succesfull reconnection occurs, or when a user cancels it (see `OnConnectFinished` and `OnUserDisconnectRequest`). +The reconnecting state is entered when a client disconnects unexpectedly. (See `OnClientDisconnect` in [this class](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Assets/Scripts/ConnectionManagement/ConnectionState/ClientConnectedState.cs)) :::note This sample connects with [Lobby](https://docs.unity.com/lobby/unity-lobby-service-overview.html) and [Relay](https://docs.unity.com/relay/get-started.html) services, so the client must make sure it has properly left the lobby before each reconnection attempt. diff --git a/docs/learn/bitesize/bitesize-spaceshooter.md b/docs/learn/bitesize/bitesize-spaceshooter.md index 7b3a71e37..0b4cdbc8c 100644 --- a/docs/learn/bitesize/bitesize-spaceshooter.md +++ b/docs/learn/bitesize/bitesize-spaceshooter.md @@ -12,7 +12,7 @@ The movement in 2DSpaceShooter is physics based. The player object is a dynamic The client sends inputs in the form of RPCs to the server. The server then uses those inputs to adjust the throttle and turn values of the player. -This method of running physics makes sure that there are no desyncs or other physics issues between the client and server, but it introduces more latency. With future prediction support of Netcode, the latency will no longer be an issue which makes this the best choice of a movement model for a game like this. +This method of running physics makes sure that there are no desyncs or other physics issues between the client and server, but it introduces more latency. ## Player Health diff --git a/docs/learn/bossroom/getting-started-boss-room.md b/docs/learn/bossroom/getting-started-boss-room.md index e5b151cd4..de248940c 100644 --- a/docs/learn/bossroom/getting-started-boss-room.md +++ b/docs/learn/bossroom/getting-started-boss-room.md @@ -6,13 +6,9 @@ description: Learn more about installing and running the Boss Room game sample. ![Boss Room banner](/img/banner.png) -:::important -This project is currently in early-access. -::: - -Boss Room is a fully functional Multiplayer Co-op Sample game made with Unity and Netcode for GameObjects (Netcode). We are building the sample to serve as an educational showcase of specific typical gameplay patterns frequently featured in similar games. +Boss Room is a fully functional co-op multiplayer RPG made with Unity Netcode. It is an educational sample designed to showcase typical netcode patterns that are frequently featured in similar multiplayer games. -You can use everything in this project as a starting point or as bits and pieces in your own Unity games. +PLEASE NOTE: Boss Room is compatible with the latest Unity Long Term Support (LTS) editor version, currently 2021 LTS. Please include standalone support for Windows/Mac in your installation. ## Download the project files @@ -27,22 +23,26 @@ Using Windows' built-in extracting tool may generate an "Error 0x80010135: Path 1. You can now add the Boss Room project to your Unity Hub. :::important Compatibility -- Boss Room supports those platforms supported by Netcode. -- Boss Room is compatible with Unity 2020.3 and later. -- Ensure your Unity installation includes the build support module components for your OS. +Boss Room has been developed and tested on the following platforms: + +* Windows +* Mac +* iOS +* Android +Boss Room's min spec devices are: + +* iPhone 6S +* Samsung Galaxy J2 Core ::: ## Add the project with Unity Hub 1. Open your Unity Hub. 1. Click the dropdown arrow next to **Open**, then select **Add project from disk**. -1. Select the root folder of the downloaded project. For example, `com.unity.multiplayer.samples.coop-0.2.1`. - - +1. Select the root folder of the downloaded project. For example, `com.unity.multiplayer.samples.coop-`. :::note -The first time you open the project, Unity will import all assets, which will take longer than usual - this is normal. The Unity Netcode For GameObjects v1.0.0 Netcodepackage will also be installed with Boss Room. +The first time you open the project, Unity will import all assets, which will take longer than usual - this is normal. The Unity Netcode for GameObjects package will also be installed with Boss Room. **Issues with importing due to parental control software**: If you have issues with importing you may want to check your DNS settings as some ISP parental controls may block GitHub access. For example, see this information on [WebSafe](https://community.virginmedia.com/t5/Networking-and-WiFi/Web-Safe-Breaks-GitHub/td-p/4279652). ::: @@ -60,10 +60,6 @@ To open the project for the first time: 2. Then under the **Project** tab, go to **Assets** > **Boss Room** > **Scenes** and double-click on the **Startup** scene. 3. Click **Play**. The Boss Room menu scene loads. - - - ## Test multiplayer To see the multiplayer functionality in action, you can either run multiple instances of the game [locally on your computer](#local-multiplayer-setup) or choose to [connect through the internet](#multiplayer-over-internet). @@ -75,20 +71,14 @@ For a local multiplayer setup, you must build an executable and launch several i 1. With the Boss Room project open in your Unity editor, click **File** > **Build Settings** > **Build**. 2. Save the binary as `Boss Room`. - - After the build has completed, you can launch several instances of the built executable to both host and join a game. :::important Mac Users To run multiple instances of the same app, you need to use the command line: -1. First, change your directory to the folder where you saved the Boss Room executable. For example, `cd Desktop/com.unity.multiplayer.samples.coop-0.2.1`. +1. First, change your directory to the folder where you saved the Boss Room executable. For example, `cd Desktop/com.unity.multiplayer.samples.coop-`. 2. Run the command `Open -n YourAppName.app`. If you saved the app as `BossRoom`, your command is `Open -n BossRoom.app`. However, if you saved your app as `Boss Room` with a space, your command needs to include quotation marks ("") around the executable name: `Open -n "BossRoom.app"`. ::: - - ## Multiplayer over internet In contrast to running a local setup, when playing over internet we do not necessarily need a built executable. You can run the game in editor. From 0c6e03a7ea411e10680b78d3ae30601e5726f592 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Fri, 7 Oct 2022 12:57:51 -0500 Subject: [PATCH 2/5] Start 1.1.0 updates Just creating a branch to start working on v1.1.0 updates --- docs/basics/scenemanagement/inscene-placed-networkobjects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basics/scenemanagement/inscene-placed-networkobjects.md b/docs/basics/scenemanagement/inscene-placed-networkobjects.md index 1239737d8..159abd372 100644 --- a/docs/basics/scenemanagement/inscene-placed-networkobjects.md +++ b/docs/basics/scenemanagement/inscene-placed-networkobjects.md @@ -104,7 +104,7 @@ Using this approach allows you to: While we encourage using in-scene placed `NetworkObject`s as something static, a manager, or a combination of both (a hybrid approach), there are times where you might need to use an in-scene placed `NetworkObjet` more like a dynamically spawned `NetworkObject` for other purposes. For every purpose you can imagine, more often than not you might be better off using a hybrid approach and using an in-scene placed `NetworkObject` to dynamically spawn a `NetworkObject`. ::: -### De-spawning, Re-spawning, and Parenting +### De-spawning, Re-spawning, and Parenting (TODO: Add updates for v1.1.0) - You can de-spawn and re-spawn them on the server-side - You can parent them (with caution) - If you plan on being able to un-parent (i.e. drop an item, etc.) a child `NetworkObject`, then a dynamically spawned `NetworkObject` is a better choice
From 2660355c70c5a9ddd70b3d59bc772bedc1eb357b Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Fri, 7 Oct 2022 16:56:29 -0500 Subject: [PATCH 3/5] update updated the spawning and despawning portion. --- .../inscene-placed-networkobjects.md | 130 ++++++++++++++++-- 1 file changed, 121 insertions(+), 9 deletions(-) diff --git a/docs/basics/scenemanagement/inscene-placed-networkobjects.md b/docs/basics/scenemanagement/inscene-placed-networkobjects.md index 159abd372..0c62b796e 100644 --- a/docs/basics/scenemanagement/inscene-placed-networkobjects.md +++ b/docs/basics/scenemanagement/inscene-placed-networkobjects.md @@ -104,15 +104,127 @@ Using this approach allows you to: While we encourage using in-scene placed `NetworkObject`s as something static, a manager, or a combination of both (a hybrid approach), there are times where you might need to use an in-scene placed `NetworkObjet` more like a dynamically spawned `NetworkObject` for other purposes. For every purpose you can imagine, more often than not you might be better off using a hybrid approach and using an in-scene placed `NetworkObject` to dynamically spawn a `NetworkObject`. ::: -### De-spawning, Re-spawning, and Parenting (TODO: Add updates for v1.1.0) -- You can de-spawn and re-spawn them on the server-side -- You can parent them (with caution) - - If you plan on being able to un-parent (i.e. drop an item, etc.) a child `NetworkObject`, then a dynamically spawned `NetworkObject` is a better choice
-:::warning -There is a known bug where an in-scene placed `NetworkObject` that dynamically spawns one or more `NetworkObject`(s) and then immediately parents them under its root `GameObject` (or any child) can cause issues with client synchronization and the spawned children. It is advised to provide a few frames, after the parent in-scene placed `NetworkObject` has spawned and has spawned its children, before parenting the children under the in-scene placed `NetworkObject`. Use (if at all) parenting in-scene placed `NetworkObject`s with caution as there could be more edge case scenario bugs. (_The hybrid approach is the recommended path to take._) +### Spawning and De-spawning +By default, an in-scene placed `NetworkObject` will always get spawned when the scene it was placed within is loaded and a network session is in progress. However, in-scene placed `NetworkObject`s are unique from dynamically spawned `NetworkObject`s when it comes to spawning and de-spawning. Functionally speaking, when de-spawning a dynamically spawned NetworkObject you can always spawn a new instance of the `NetworkObject`'s associated network prefab. So, whether you decide to destroy a dynamically spawned `NetworkObject` or not, you can always make another clone of the same network prefab unless you want to preserve the current state of the instance being de-spawned.
+With in-scene placed NetworkObjects, the scene it is placed within is similar to the network prefab used to dynamically spawn a `NetworkObject` in that both are used to uniquely identify the spawned `NetworkObject`. The primary difference is that you use a network prefab to create a new dynamically spawned instance where you a required to additively load the same scene to create another in-scene placed `NetworkObject` instance.
+ +**How the two types of spawned `NetworkObject`s are uniquely identified** + +Dynamically Spawned | In-Scene Placed +------------------- | --------------- +NetworkPrefab | Scene (_When Loaded, the Scene's Handle_) +GlobalObjectIdHash | GlobalObjectIdHash + +Once the `NetworkObject` is spawned, Netcode for GameObjects uses the `NetworkObjectId` to uniquely identify it for both types. + +
+ +**_What if you wanted to de-spawn and re-spawn the same in-scene placed NetworkObject?_** + +When invoking the `Despawn` method of a `NetworkObject` with no parameters, it will always default to destroying the `NetworkObject`: + +```csharp +NetworkObject.Despawn(); // Will de-spawn and destroy +``` + +If you want an in-scene placed NetworkObject to persist after it has been de-spawned, it is recommended to always set the first parameter of the `Despawn` method to `false`: + +```csharp +NetworkObject.Despawn(false); // Will only de-spawn +``` + +Now that you have a de-spawned `NetworkObject`, you might notice that the associated `GameObject` and all of its components are still active and possibly visible to all players (i.e. like a `MeshRenderer` component). Unless you have a specific reason to keep the associated `GameObject` active in the hierarchy, you can override the virtual `OnNetworkDespawn` method in a `NetworkBehaviour` derived component and set the `GameObject` to in-active: + +```csharp +using UnityEngine; +using Unity.Netcode; + +public class MyInSceneNetworkObjectBehaviour : NetworkBehaviour +{ + public override void OnNetworkDespawn() + { + gameObject.SetActive(false); + base.OnNetworkDespawn(); + } +} +``` + +This will assure that when your in-scene placed `NetworkObject` is de-spawned it won't consume precious processing or rendering cycles and it will become "invisible" to all players (connected or that join the session later). Once the `NetworkObject` has been de-spawned and disabled, you might want to re-spawn it at some later time. To do this, you would want to set the server-side instance's `GameObject` back to being active and spawn it: + +```csharp +using UnityEngine; +using Unity.Netcode; + +public class MyInSceneNetworkObjectBehaviour : NetworkBehaviour +{ + public override void OnNetworkDespawn() + { + gameObject.SetActive(false); + base.OnNetworkDespawn(); + } + + public void Spawn(bool destroyWithScene) + { + if (IsServer && !IsSpawned) + { + gameObject.SetActive(true); + NetworkObject.Spawn(destroyWithScene); + } + } +} +``` + +:::info +You only need to enable the `NetworkObject` on the server-side to be able to re-spawn it. Netcode for GameObjects will only enable a disabled in-scene placed `NetworkObject` on the client-side if the server-side spawns it.
+_This **does not** apply to dynamically spawned `NetworkObjects`.
(see [Object Pooling](../../advanced-topics/object-pooling.md) for an example of recycling dynamically spawned `NetworkObject`s_) ::: + + +**_How can you start an in-scene placed `NetworkObject` as de-spawned when the scene is first loaded (i.e. its first spawn)?_** + +Since in-scene placed `NetworkObject`s are automatically spawned when their respective scene has finished loading during a network session, you might run into the scenario where you want it to start disabled until a certain condition has been met specific to your project. To do this, it only requires adding some additional code in the `OnNetworkSpawn` portion of your `NetworkBehaviour` component: + +```csharp +using UnityEngine; +using Unity.Netcode; + + public class MyInSceneNetworkObjectBehaviour : NetworkBehaviour + { + public bool StartDespawned; + + private bool m_HasStartedDespawned; + public override void OnNetworkSpawn() + { + if (IsServer && StartDespawned && !m_HasStartedDespawned) + { + m_HasStartedDespawned = true; + NetworkObject.Despawn(); + } + base.OnNetworkSpawn(); + } + + public override void OnNetworkDespawn() + { + gameObject.SetActive(false); + base.OnNetworkDespawn(); + } + + public void Spawn(bool destroyWithScene) + { + if (IsServer && !IsSpawned) + { + gameObject.SetActive(true); + NetworkObject.Spawn(destroyWithScene); + } + } + } +``` + +You will notice the above example keeps track of whether the in-scene placed `NetworkObject` has started as being de-spawned (to avoid de-spawning after its first time being spawned), and it makes sure only the server executes that block of code in the overridden `OnNetworkSpawn` method. The above `MyInSceneNetworkObjectBehaviour` example also declares a public `bool` property `StartDespawned` to provide control over this through the inspector view in the editor. + + +### Parenting +(WIP) +- You can parent and remove the parent like you would with dynamically spawned `NetworkObject`s
-:::tip -While you can parent in-scene placed `NetworkObject`s within the editor, you might stop to think about what you are trying to accomplish. A child `NetworkBehaviour` will be assigned to the first parent `GameObject` with a `NetworkObject` component. _You might be able to accomplish the same thing with a single in-scene placed `NetworkObject` as opposed to several nested `NetworkObject`s._ -::: From 65dc0e7829bee7f63ca338ee33ce5582bdf6cad2 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Tue, 25 Oct 2022 18:26:29 -0500 Subject: [PATCH 4/5] update Final first pass for in-scene placed NetworkObject documentation updates. --- .../inscene-placed-networkobjects.md | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/docs/basics/scenemanagement/inscene-placed-networkobjects.md b/docs/basics/scenemanagement/inscene-placed-networkobjects.md index 0c62b796e..0e95bbc04 100644 --- a/docs/basics/scenemanagement/inscene-placed-networkobjects.md +++ b/docs/basics/scenemanagement/inscene-placed-networkobjects.md @@ -48,7 +48,7 @@ Since there are additional complexities involved with in-scene placed `NetworkOb ### Static Objects There are many scenarios where you might just need to use the in-scene placed `NetworkObject` to keep track of when a door is opened, a button pushed, a lever pulled, and any other "toggle oriented" type of state. This is what we consider "static objects": - - They are "statically" spawned while its originating scene is loaded and only despawned when its originating scene is unloaded. + - They are "statically" spawned while its originating scene is loaded and only de-spawned when its originating scene is unloaded. - The originating scene is the scene that the in-scene `NetworkObject` was placed. - They are typically associated with some world object that is visible to the players (i.e. door, switch, etc.) - They aren't migrated into other scenes or parented under another `NetworkObject` @@ -58,7 +58,7 @@ There are many scenarios where you might just need to use the in-scene placed `N ### Netcode Managers An in-scene placed `NetworkObject` used as a netcode manager could range from handling game states (i.e. player scores) to a `NetworkObject` spawn manager (pooled or not). Typically, a manager will stay instantiated and spawned as long as the scene it was placed in remains loaded. For scenarios where you want to keep a global game state, the recommended solution is to place the in-scene `NetworkObject` in an additively loaded scene that remains loaded for the duration of your network game session. -If you are using "Scene Switching" (i.e. loading a scene in LoadSceneMode.Single), then you can migrate the in-scene placed `NetworkObject` into the DDoL by sending its `GameObject` to the DDoL like in the code snippet below: +If you are using "Scene Switching" (i.e. loading a scene in LoadSceneMode.Single), then you can migrate the in-scene placed `NetworkObject` (used for management purposes) into the DDoL by sending its `GameObject` to the DDoL: ```csharp private void Start() @@ -67,21 +67,19 @@ private void Start() DontDestroyOnLoad(gameObject); } ``` + :::warning -Once migrated into the DDoL, migrating the in-scene placed `NetworkObject` back into a different scene after it has already been spawned will cause soft synchronization errors with late joining clients. Once in the DDoL it should stay in the DDoL. This is only for scene switching, if you are not using scene switching then it is recommended to use an additively loaded scene and keep that scene loaded for as long as you wish to persist the in-scene placed `NetworkObject`(s) in question. +Once migrated into the DDoL, migrating the in-scene placed `NetworkObject` back into a different scene after it has already been spawned will cause soft synchronization errors with late joining clients. Once in the DDoL it should stay in the DDoL. This is only for scene switching, if you are not using scene switching then it is recommended to use an additively loaded scene and keep that scene loaded for as long as you wish to persist the in-scene placed `NetworkObject`(s) being used for state management purposes. ::: -While using an in-scene placed `NetworkObject` as a manager can have some complexities involved when you wish to persist it while also using "Scene Switching", this is still considered one of the least complex ways to use an in-scene placed `NetworkObject`. - -### Complex In-Scene NetworkObject Managers: +### Complex In-Scene NetworkObjects: The most common mistake when using an in-scene placed `NetworkObject` is to try and use it like a dynamically spawned `NetworkObject`. When trying to decide if you should use an in-scene placed or dynamically spawned `NetworkObject`, you should ask yourself the following questions: -- Will it be spawned and despawned more than once? +- Do you plan on de-spawning and destroying the `NetworkObject`? _(highly recommended to use dynamically spawned)_ - Could it be parented, at runtime, under another `NetworkObject`? - - Does the parent exist in a different scene than the originating scene of the in-scene placed `NetworkObject'? - Excluding any special case DDoL scenarios, will it be moved into another scene other than the originating scene? -- Does it dynamically spawn NetworkObjects and then immediately parent the spawned instances under itself? +- Do you plan on having full scene-migrations (i.e. LoadSceneMode.Single or "scene switching") during a network session? -If you answered yes to any of the above questions, then using only and in-scene placed `NetworkObject` to implement your feature is most likely not the right choice. However, just because you did answer yes to one or more of the above questions doesn't mean you shouldn't use an in-scene placed `NetworkObject` either. While the previous two sentences might seem puzzling, there are scenarios where the "best choice" (regarding simplicity and modularity) is to use a hybrid approach by using a combination of both! +If you answered yes to any of the above questions, then using only an in-scene placed `NetworkObject` to implement your feature might not be the right choice. However, just because you did answer yes to one or more of the above questions doesn't mean you shouldn't use an in-scene placed `NetworkObject` either. While the previous two sentences might seem puzzling, there are scenarios where the "best choice" (regarding simplicity and modularity) is to use a hybrid approach by using a combination of both in-scene placed and dynamically spawned `NetworkObject`s! #### A Hybrid Approach Example Perhaps your project's design includes making some world items that can either be consumed (i.e. health) or picked up (weapons, items, etc) by players. Initially, using a single in-scene placed `NetworkObject` might seem like the best approach for this world item feature. @@ -98,11 +96,7 @@ Using this approach allows you to: 1. Re-use the same single spawn manager with any other Network Prefab registered with the `NetworkManager` 2. Not worry about the complexities involved with treating an in-scene placed `NetworkObject` like a dynamically spawned one. -[See a Dynamic Spawning (non-pooled) Example Here](../object-spawning#dynamic-spawning-non-pooled) - -:::important -While we encourage using in-scene placed `NetworkObject`s as something static, a manager, or a combination of both (a hybrid approach), there are times where you might need to use an in-scene placed `NetworkObjet` more like a dynamically spawned `NetworkObject` for other purposes. For every purpose you can imagine, more often than not you might be better off using a hybrid approach and using an in-scene placed `NetworkObject` to dynamically spawn a `NetworkObject`. -::: +[See a Dynamic Spawning (non-pooled) "Hybrid Approach" Example Here](../object-spawning#dynamic-spawning-non-pooled) ### Spawning and De-spawning By default, an in-scene placed `NetworkObject` will always get spawned when the scene it was placed within is loaded and a network session is in progress. However, in-scene placed `NetworkObject`s are unique from dynamically spawned `NetworkObject`s when it comes to spawning and de-spawning. Functionally speaking, when de-spawning a dynamically spawned NetworkObject you can always spawn a new instance of the `NetworkObject`'s associated network prefab. So, whether you decide to destroy a dynamically spawned `NetworkObject` or not, you can always make another clone of the same network prefab unless you want to preserve the current state of the instance being de-spawned.
@@ -112,10 +106,10 @@ With in-scene placed NetworkObjects, the scene it is placed within is similar to Dynamically Spawned | In-Scene Placed ------------------- | --------------- -NetworkPrefab | Scene (_When Loaded, the Scene's Handle_) -GlobalObjectIdHash | GlobalObjectIdHash +NetworkPrefab | Scene +GlobalObjectIdHash | Scene Handle (_When Loaded_) & GlobalObjectIdHash -Once the `NetworkObject` is spawned, Netcode for GameObjects uses the `NetworkObjectId` to uniquely identify it for both types. +Once the `NetworkObject` is spawned, Netcode for GameObjects uses the `NetworkObjectId` to uniquely identify it for both types. An in-scene placed `NetworkObject` will still continue to be uniquely identified by the scene handle that it originated from and the GlobalObjectIdHash even if the in-scene placed `NetworkObject` is migrated to another additively loaded scene and originating scene is unloaded.
@@ -124,13 +118,13 @@ Once the `NetworkObject` is spawned, Netcode for GameObjects uses the `NetworkOb When invoking the `Despawn` method of a `NetworkObject` with no parameters, it will always default to destroying the `NetworkObject`: ```csharp -NetworkObject.Despawn(); // Will de-spawn and destroy +NetworkObject.Despawn(); // Will de-spawn and destroy (!!! not recommended !!!) ``` If you want an in-scene placed NetworkObject to persist after it has been de-spawned, it is recommended to always set the first parameter of the `Despawn` method to `false`: ```csharp -NetworkObject.Despawn(false); // Will only de-spawn +NetworkObject.Despawn(false); // Will only de-spawn (recommended usage for in-scene placed NetworkObjects) ``` Now that you have a de-spawned `NetworkObject`, you might notice that the associated `GameObject` and all of its components are still active and possibly visible to all players (i.e. like a `MeshRenderer` component). Unless you have a specific reason to keep the associated `GameObject` active in the hierarchy, you can override the virtual `OnNetworkDespawn` method in a `NetworkBehaviour` derived component and set the `GameObject` to in-active: @@ -198,7 +192,7 @@ using Unity.Netcode; if (IsServer && StartDespawned && !m_HasStartedDespawned) { m_HasStartedDespawned = true; - NetworkObject.Despawn(); + NetworkObject.Despawn(false); } base.OnNetworkSpawn(); } @@ -220,11 +214,19 @@ using Unity.Netcode; } ``` -You will notice the above example keeps track of whether the in-scene placed `NetworkObject` has started as being de-spawned (to avoid de-spawning after its first time being spawned), and it makes sure only the server executes that block of code in the overridden `OnNetworkSpawn` method. The above `MyInSceneNetworkObjectBehaviour` example also declares a public `bool` property `StartDespawned` to provide control over this through the inspector view in the editor. +You will notice the above example keeps track of whether the in-scene placed `NetworkObject` has started as being de-spawned (to avoid de-spawning after its first time being spawned), and it only allows the server to execute that block of code in the overridden `OnNetworkSpawn` method. The above `MyInSceneNetworkObjectBehaviour` example also declares a public `bool` property `StartDespawned` to provide control over this through the inspector view in the editor. +**_How do I synchronize late joining clients when an in-scene placed `NetworkObject` has been de-spawned and destroyed?_** -### Parenting -(WIP) -- You can parent and remove the parent like you would with dynamically spawned `NetworkObject`s -
+Referring back to the [Complex In-Scene NetworkObject Managers](inscene-placed-networkobjects#complex-in-scene-networkobjects), it is recommended to use dynamically spawned `NetworkObject`s if you are planning on destroying the object when it is de-spawned. However, if either de-spawning but not destroying or using the hybrid approach ([discussed earlier on this page](inscene-placed-networkobjects#a-hybrid-approach-example)) (dynamically spawned) don't appear to be options for your project's needs, then really there are only two other possible (but not recommended) alternatives: +- Have another in-scene placed `NetworkObject` track which in-scene placed `NetworkObject`s have been destroyed and upon a player late-joining (i.e. 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 does not 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" *are not recommended* but worth briefly exploring to better understand why we recommend using a [non-pooled hybrid approach](../object-spawning#dynamic-spawning-non-pooled) or just not destroying the in-scene placed `NetworkObject` when de-spawning it. _The time spent implementing and possibly debugging either of the two above not recommended approaches will far exceed the time spent implementing one of the recommended approaches._ +### Parenting +In-scene placed `NetworkObject`s follow the same parenting rules as [dynamically spawned `NetworkObject`s](../../advanced-topics/networkobject-parenting.md) with only a few differences and recommendations: +- You can create complex nested `NetworkObject` hierarchies when they are in-scene placed. +- If you plan on using full scene-migration (i.e. LoadSceneMode.Single or "scene switching") then parenting an in-scene placed `NetworkObject` that stays parented during the scene migration is not recommended. + - Under this scenario, you would want to use a hybrid approach where the in-scene placed `NetworkObject` dynamically spawns the item to be picked up. +- If you plan on using a bootstrap scene usage pattern where you use additive scene loading and unloading with no full scene-migration(s), then it is "OK" to parent in-scene placed NetworkObjects. From 90ea4e91abb17527e2fe598a81784bd1181d8108 Mon Sep 17 00:00:00 2001 From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com> Date: Thu, 27 Oct 2022 12:44:51 -0500 Subject: [PATCH 5/5] update adding temporary warning to users about in-scene placed NetworkObject parenting. --- docs/basics/scenemanagement/inscene-placed-networkobjects.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/basics/scenemanagement/inscene-placed-networkobjects.md b/docs/basics/scenemanagement/inscene-placed-networkobjects.md index 0e95bbc04..e3e758380 100644 --- a/docs/basics/scenemanagement/inscene-placed-networkobjects.md +++ b/docs/basics/scenemanagement/inscene-placed-networkobjects.md @@ -230,3 +230,7 @@ In-scene placed `NetworkObject`s follow the same parenting rules as [dynamically - If you plan on using full scene-migration (i.e. LoadSceneMode.Single or "scene switching") then parenting an in-scene placed `NetworkObject` that stays parented during the scene migration is not recommended. - Under this scenario, you would want to use a hybrid approach where the in-scene placed `NetworkObject` dynamically spawns the item to be picked up. - If you plan on using a bootstrap scene usage pattern where you use additive scene loading and unloading with no full scene-migration(s), then it is "OK" to parent in-scene placed NetworkObjects. + +:::warning +Parenting in-scene placed `NetworkObject`s under `GameObject`s with no `NetworkObject` component will currently synchronize the child `NetworkObject` as if it is in world space on the client side. To work around this particular issue you should add a `NetworkTransform` to the child and enable local space synchronization. +::: \ No newline at end of file