Hi everyone,
I am currently running into an issue with dynamically spawning networked prefabs that are nested in non-networked prefabs. The type of hierarchy I am talking about looks like something like this (bold are prefabs):
-
GO1 (No NetworkObject)
-
GO2 (NetworkObject)
-
GO4 (No NetworkObject)
-
GO3 (NetworkObject)
-
…
where both GO2 and GO3 are with the NetworkManager registered prefabs.
Then GO1 is instantiated, all children with NetworkObjects are collected and spawned (from the host).
What I want (and what I would expect) to happen is that then both GO2 and GO3 (with GO4) are replicated across all clients and GO1 only exists on the host.
However, the result is that the client is not able to spawn GO2 and GO3, and the following exceptions are thrown:
NetworkPrefab could not be found. Is the prefab registered with NetworkManager?```
```[Netcode] Failed to spawn NetworkObject for Hash 3448903606.```
```NullReferenceException: Object reference not set to an instance of an object
Unity.Netcode.NetworkMetrics.GetObjectIdentifier ...```
I am not sure whether this is allowed, but the documentation seems to only disallow the case where a networked prefab is nested inside another networked prefab?
Hi @salvator23 ,
Parenting a dynamically spawned NetworkObject under a GameObject with no NetworkObject component is not considered a valid action as the root parent GameObject could have adjusted transform values that could impact the children transform values (amongst other issues).
Could you describe what you are trying to accomplish with this type of layout and perhaps I could offer an alternate but similar approach?
Hi and thanks for your quick answer!
The context is that I am currently working on making custom levels (created with an in-game editor) work on multiplayer.
The current set-up is that I have a list of prefabs, which can be structured like the example GO1 from above (with children), and both the host and the client already have the information which prefabs need to be instantiated.
My idea was that the client instantiates all non-networked gameobjects of the prefab (i.e., only GO1), while the host additionally spawns all networked objects (i.e., GO1 instantiated, GO2 and GO3 instantiated+spawned).
The hierarchy does not need to be correctly replicated on the clients for my case, but having prefabs like GO1 enables me to provide pre-grouped objects for the mapeditor, in additition to the children as their own mapeditor object.
Well, I think you might want to leverage from a “feature” that isn’t really documented as one can get “tangled” if not careful. However, if you are building an in-game level editor then I think you should be able to track where everything is located.
Here is the trick for moving things around:
Once spawned, a NetworkBehaviour’s association with a NetworkObject is constant. The whole “real” relationship between a NetworkBehaviour and NetworkObject is that the NetworkObjectId is like the beginning of an IP address and the NetworkBehaviourId is just the next segment of that address that gets a message to the right class instance. NetworkVariables have internal identifiers… and RPCs have a table lookup associated with a class.
So…
This relationship will be maintained no matter where the GameObject that a NetworkBehaviour component is attached to is located while a NetworkObject is spawned. This means… (you probably know where this is heading)
Take your example:
-
GO1 (No NetworkObject)
-
GO2 (NetworkObject)
-
GO4 (No NetworkObject)
-
GO3 (NetworkObject)
As opposed to doing that, you could make your “pieces/parts” prefabs look something like this:
-
GO2Prefab (GameObject)(NetworkObject)
-
__GO2-Behaviour (__GameObject)(NetworkBehaviour)
-
GO3Prefab (GameObject)(NetworkObject)
-
**GO3-Behaviour (GameObject)(**NetworkBehaviour)
The central idea is you can have as many nested children generation (GameObject)(NetworkBehaviour)'s as you want as long as the root GameObject has a NetworkObject component.
The extension of this idea is that you can place (GameObject)(NetworkBehaviour)s under any GameObject once the NetworkObject is spawned.
So, converting your example over to the above format and after you have spawned your user’s “pieces/parts” (i.e. GO2Prefab and GO3Prefab):
-
GO1 (No NetworkObject)
-
**GO2-Behaviour (**GameObject)(NetworkBehaviour)
-
GO4 (No NetworkObject)
-
GO3-Behaviour (GameObject)(NetworkBehaviour)
-
**GO2Prefab (GameObject)(**NetworkObject)
-
**GO3Prefab (**GameObject)(NetworkObject)
You would just use normal transform parenting of the child (GameObject)(NetworkBehaviour)s and messages will still be sent/received and all will work out as you would expect.
You will still have the Root network prefab instances hanging around, but as long as they don’t have anything but basically a GameObject and NetworkObject then they won’t really be noticeable. If you want to be a bit “cleaner” with the approach then just make an in-scene placed NetworkObject that is your manager of the spawned network prefab “pieces/parts” instances and before migrating their children over to the above hierarchy you would just parent them under the manager. Then you could create a single NetworkBehavior (let’s call it the “ParentingManagementBehaviour”) at the root GameObject of the prefab that has a NetworkObject component with script that upon being parented (i.e. OnNetworkObjectParentChanged) and the parent is not null then you migrate the child (GameObject)(NetworkBehaviour) to its proper user defined location and if it is null it re-parents the (GameObject)(NetworkBehaviour) back under itself. The in-scene placed NetworkObject really doesn’t need to have any special “logic” for this to work properly…it is just a location to keep the Root network prefab instances when their children are parented elsewhere. Conveniently, it also happens to provide an automated mechanism that triggers the ParentingManagementBehaviour’s script logic that handles the parenting of its child (or children) under a specific GameObject in your scene. The ParentingManagementBehaviour can also hold the child parenting destination information on each instance…making it sort of an “all-in-one” solution to a “lego-like” framework.
Using that you would end up with something like this when everything is parented:
-
GO1 (No NetworkObject)
-
**GO2-Behaviour (**GameObject)(NetworkBehaviour)
-
GO4 (No NetworkObject)
-
GO3-Behaviour (GameObject)(NetworkBehaviour)
-
GOPrefabRootContainer (GameObject)(NetworkObject)(In-Scene Placed)
-
**GO2Prefab (**GameObject)(NetworkObject)
-
GO3Prefab (GameObject)(NetworkObject)
Let me know if this direction makes sense (or if you have any other questions feel free to ask away).
Happy Netcoding!
I have implemented and tested a small example using your suggestions, and it seems to be working fine so far.
Thank you for the fast and helpful answers!
1 Like
Awesome!
If you run into any issues feel free to reach out.
;)
It’s a shame, looks like Unity’s new discussion forum doesn’t format your post properly? The bullet points appear odd. This was a good post