Network object having a child with deactivated Network Object - Problem at despawn

Hi,

In my game, objects can be autonomous / spawned in the world (example, an axe laying on the ground), or attached to an other object (example : an axe in the hand of a creature).
I use the same prefab for both usage, and this prefab has a Network Object and Network behaviours scripts

In the second case, I manually manage the network synchronization using Network Variables
On each client, I create the Axe object, I disable its Network Object (I cannot remove it because several NetworkBehaviours prevent me from doing that).
Everything works fine. The network object on the creature is enabled and spawned, and the network object on the axe is not enabled and not spawned.

However when I despawn the creature, I get a warning on the server

Network Object (axe) moved to the root because its parent Network object (creature) is destroyed

and the axe is moved to the root on server, unspawned, causing bug & desynchronization with clients.

Looking at NetworkSpawnManager.OnDespawnObject, there is an iteration over all NetworkObject childs, but no check that this network object is active or spawned.
I can fix it with a simple :
if (!spawnedNetObj.IsSpawned) continue;

But I would like to have community feedback about this :

  • Do you have other way to use a prefab containing a network object, and deactivating its network object ?
  • Do you see any drawback in the proposed fix ?
  • Should this fix be integrated in the standard netcode code ? (Am I the only one using nested unspawned network object ? Is there a sense to move an unspawned network object to the root ?)

Thanks in advance !
Thomas

Don’t do this!

The proper way to deal with objects that have a in-world and other representation (eg Inventory):

  • Create a general purpose In-World Item NetworkObject prefab
  • Create a general purpose Inventory Item NetworkObject prefab (optional because Inventory items need no synchronization, they’re typically entirely client-side only and at best you’d have a NetworkList of item indexes the player currently has in the Inventory)
  • Neither prefab contains any renderer.
  • Prefabs may either contain a unified Collider (same for all types) or no Collider
  • For each item type, you create a prefab with the renderer and (optional) Collider
  • Upon spawning either network object, you pass in the index of the item it represents

Typically you’ll use an index to a ScriptableObject’s list of items. The index can be specified on design time (in-scene placed) or passed via RPC/Netvar. These item objects then lookup the visual representation prefab in the SO and Instantiate it as a children of itself - wholly non-networked.

If the visuals of the item needs to get events like OnNetworkSpawn you’d just forward this to any child script implementing a given interface. Likewise the child script could look up its parent NetworkObject and register for events with any other component on that object, such as “OnPickup” or “OnDrop” which can be used to play a sound fx for instance.

In the editor, you could have an editor script that instantiates the visual representation of the object with “HideAndDontSave” hideflags such that the visualization does not get saved to the scene just so you don’t have to guess what Item 123 is, nor would you have to look at the Inspector.

Hello CodeSmile,
Thanks for your quick answer !

Agree on the “proper way”. Globally to split as much as possible rendering and logic.
In my case, I don’t talk about the inventory management and it’s synchronization, it’s an other subject, and without any renderer :wink:

I’m only talking about the rendering part
And this rendering share several behaviours between “in world” and “in hand” (collisions sounds depending on material, particles shown on equipment depending on the state, weight management, …).
That’s why I use the same prefab for both rendering, and this prefab have NetworkBehaviours and a NetworkObject.
I only have some checks “if (IsSpawned) …” to synchronize state or not depending on the case.

I know, it’s possible to split code, having “Item in world” classes based on NetworkBehaviour, and “Item in hand” classes based on Monobehaviour, and put common code in shared classes.
But :

  • It concerns hundreds of items, so lot of work to duplicate prefabs, etc… Far easier to create and maintain using the same prefab
  • The equipment system works well for almost a year now with tests sessions with up to 10 players playing in parallel
  • My only problem is this useless (for me) treatment executed by Netcode when the parent is despawned, raising a warning and requiring me to manually destroy the equipment.
  • Also I cannot see the sense to make this treatment if the child network object is not spawned. Not spawned = only on server side, what the sense to move it at the root ?

(Also I don’t take in account the performance of parsing all children tree to find network object, allocate an array to put is in, … ). If it were possible I would have loved to have a parameter on the NetworkObject to completly discard this functionnality, cause 99.9% of my network objects don’t need that.

Not sure I understand.

If you currently have 100 networked item prefabs, all you would have to do is to create one generic networked item prefab, and then remove all network components from the 100 item prefabs, and lastly use the generic network prefab to spawn a given item prefab.

If you mean to say that you have 100 item representations (visuals) in a single prefab, and just enable the one item that the instance represents, this would be woefully inefficient. Simply because the prefab hierarchy would instantiate 99 addition game objects that aren’t needed, and in a scene this would serialize 99 additional items, bloating the scene size.

I know that, unfortunately, many 3D asset publishers set up their modular characters this way. And its gruesome to think what the cost of this is in terms of load times, disk size and execution speed.

Also best practice: don’t parent network objects. You can practically always do without. Eg a player holding a weapon is a single network object, the weapon itself does not need to be networked - this can all be routed through the player. Or a player entering a vehicle: the vehicle is the networked object, the player sitting inside is just a visual representation of the player for as long as the player “is” the vehicle.

A lot of problems arise from parenting network objects hence why (on Discord) even the NGO developers strongly recommend to not use parenting.

Hello CodeSmile,

Thanks again for taking the time to answer
In fact when I tried to better explain my problem, I discovered that I was very stupid…
All my problem was about using a Component that inherit NetworkBehaviour, and not having a NetworkObject attached (> that’s why I disabled it, etc…)
In the early tests I made, I tried to destroy the NetworkObject component, but got error “Cannot remove component, script xxx needs it”. I thought, OK it’s normal, script xxx inherit from NetworkBehaviour, I cannot remove it. So only deactivate it. But thinking it again today, there is no native RequireComponent(typeof(NetworkObject)) in NetworkBehaviour : NetworkObject are proposed to be added by an editor script.
The problem was in my own code, I added long time ago these RequireComponent(typeof(NetworkObject)) in several scripts…
Removing these allow me to destroy the NetworkObject component, and this thread is solved

Sorry to have wasted your time! :flushed:
Goreduc