Unity-Technologies / com.unity.netcode.gameobjects

Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.
MIT License
2.15k stars 435 forks source link

Despawn/Spawn functionality seems to be broken after removing Prefabhash functionality #1555

Open Shivang44 opened 2 years ago

Shivang44 commented 2 years ago

Describe the bug

It seems NetworkObjects used to have the option to manually assign a hash in a field called PrefabHash that was used to associate the hash with the prefab. This was removed.

NetworkObject now auto-generate the hash in a key called GlobalObjectIdHash based on some internal Unity3d object name.

This causes the functionality as described by the docs of calling networkObject.Despawn(false) then networkObject.Spawn() to be broken:

To despawn a networked object on all clients but keep it on the server call NetworkObject.Despawn on the server. An despawned object can also later be spawned again with another spawn call if needed.

This is because when the object is spawned client-side, a new unconfigurable hash is generated for the newly spawned object. However it cannot be known beforehand (from my understanding) what that hash will be, meaning it can't be mapped to in the NetworkManager before-hand, causing the following error when spawned on the client again:

image

Note that I do have the prefab registered in the networkmanager.

To Reproduce Steps to reproduce the behavior:

  1. Put a prefab in the scene with a networkobject script attached
  2. Register that prefab in the networkmanager
  3. On the server, call networkObject.Despawn(destroy=false) on it.
  4. On the server, call networkObject.Spawn() on it.

Actual outcome On the server, the object will be respawned. On the client, the above error will occur since the client-to-be-spawned object will have a new hash that is not mapped to anything in the networkmanager.

Expected outcome The object should be spawned on the client.

Ideally, we have some sort of prefab hash override option on NetworkObject that I can set to a specific value, and assign that value to a prefab in the NetworkManager. .

Environment (please complete the following information):

  1. I have misunderstood despawn/spawn, network prefabs, etc
  2. If attaching a sample project will help here.

Thank you!

NoelStephensUnity commented 2 years ago

Hi @Shivang44, With v1.x Netcode for GameObjects (NGO), there is a bit of a trick with respawning what we refer to "in-scene placed NetworkObjects" (whether it is a prefab or just a GameObject with a NetworkObject component added to the scene within the editor). The path of least resistance would be:

  1. create an in-scene placed NetworkObject (GameObject with a NetworkObject component)
  2. add a NetworkBehaviour derived component that contains a property of type GameObject (for this we can call it m_ObjectToSpawn) to this component.
  3. create a network prefab and register it in the NetworkManager NetworkPrefabs list
  4. assign the prefab to the m_ObjectToSpawn property of your in-scene placed NetworkObject
  5. Once the in-scene placed NetworkObject has spawned (i.e. OnNetworkSpawn) you can then instantiate an instance of the m_ObjectToSpawn, get the new instance's NetworkObject, and call Spawn on that.
  6. You can then despawn and respawn that instance as many times as you like.

To do this the way you described in your posted issue above you will have to approach this a little differently as in-scene placed NetworkObjects' GlobalObjectId values are handled slightly different due to the fact that the loading of the scene instantiates the instance outside of NGO .
To be able to respawn in-scene placed NetworkObjects: (using your steps with modifications)

  1. Put a prefab in the scene with a networkobject script attached
  2. select the prefab instance and copy the GlobalObjectId value of the prefab instance (in the scene) image
  3. Register the original prefab in the networkmanager
  4. Click the "Override" check box in that new NetworkPrefab registration entry
  5. Upon doing this, you will see two options (Prefab|Hash) select hash
  6. In the field to the right, you will see a numeric field. Paste the GlobalObjectId value you copied in step 2
  7. For the overriding prefab field, drag and drop the original prefab into that field image It should look like the above image
  8. On the server, call networkObject.Despawn(destroy=false) on it.
  9. On the server, call networkObject.Spawn() on it.

To better understand the "why" behind this, each in-scene placed NetworkObject will be assigned a unique GlobalObjectId hash value (whether it is a prefab or just a GameObject with a NetworkObject component). Because of this, the GlobalObjectId hash value of an in-scene placed Network Prefab instance will have a unique GlobalObjectId hash value that is different from the original Network Prefab that is registered with the NetworkManager. In order to re-spawn a despawned this instance, you need to link the new GlobalObjectId hash value to the original Network Prefab so the client will know which prefab to spawn.

Since respawning in-scene placed NetworkObjects will still result in a new instance being created on the client side, you might look into Object Pooling as it provides some additional control over the re-use of NetworkObjects on both the client and the server. To see working examples of this, you can clone the Netcode for GameObjects repository which will provide you with the additional testpoject that has a bunch of examples and scripts you can look at. In particular, you might look at the SceneTransitioningAdditive example that demonstrates both additive scene loading and NetworkObject pooling. image

Let me know if any of these suggestions help you?

Best Regards,

Noel

michalChrobot commented 4 days ago

@Shivang44 I'm in the process of slowly closing old or fixed issues on NGO github (so we know where to focus), it's been quite a long time since this issue was created and I wonder if the issue still exist or if the answer from Noel helped you? If this problem is not valid anymore I would like to close this issue