com.unity.netcode.gameobjects icon indicating copy to clipboard operation
com.unity.netcode.gameobjects copied to clipboard

Client NetworkObjects experience Awake, Start, OnEnable, and TriggerEnter, CollisionEnter at world 0,0,0 upon instantiation

Open zachstronaut opened this issue 5 months ago • 3 comments

Description

When a client spawns a NetworkObject via NetworkSpawnManage.InstantiateNetworkPrefab() there is no position or rotation data passed into UnityEngine.Object.Instantiate() and therefore the object is created at world 0,0,0. On a later line of code the position and rotation are assigned, after instantiation.

This means Awake, Start, and OnEnable are all called when at world 0,0,0 -- this is arguably expected and why you have OnNetworkSpawn.

However, OnTriggerEnter (and probably OnCollisionEnter) also fire for things at world 0,0,0. That problem is outside of the scope of the OnNetworkSpawn warmup in my opinion.

Developers should not have to write custom code to handle this problem when the position and rotation could simply be passed into the instantiation.

internal NetworkObject InstantiateNetworkPrefab(GameObject networkPrefab, uint prefabGlobalObjectIdHash, Vector3? position, Quaternion? rotation)
{
    Vector3 defaultPosition = position ?? Vector3.zero;
    Quaternion defaultRotation = rotation ?? Quaternion.identity;
    var networkObject = UnityEngine.Object.Instantiate(networkPrefab, defaultPosition, defaultRotation).GetComponent<NetworkObject>();
    // networkObject.transform.position = position ?? networkObject.transform.position;
    // networkObject.transform.rotation = rotation ?? networkObject.transform.rotation;
    networkObject.NetworkManagerOwner = NetworkManager;
    networkObject.PrefabGlobalObjectIdHash = prefabGlobalObjectIdHash;
    return networkObject;
}

Environment

  • OS: Windows 11
  • Unity Version: 6000.1.3f1
  • Netcode Version: 2.4.2
  • Netcode Topology: Client-Server

zachstronaut avatar Jul 17 '25 16:07 zachstronaut

@zachstronaut It is a good point, but it would most likely need to be a flag as to whether it did this or not (in order to not change the behavior so it doesn't cause other issues for other projects).

Regarding the issue you are currently having, you can solve this 0,0,0 issue a few ways:

  • Make the network prefab's default world space position be something other than 0, 0, 0.
    • It will then instantiate with that position which will avoid the trigger and collision.
  • Disable the collider on the network prefab as the default setting and enable it within OnNetworkPreSpawn.
    • If using a Rigidbody, then you would want to default to not allowing gravity and then within OnNetworkPreSpawn enable it if it is OnNetworkPreSpawn.
  • Exit early within the trigger and collider if the NetworkObject is not spawned.
    • Assuming the trigger is a spawned object...otherwise see the previous two possibilities.
  • Once #3518 has landed and in the next update, you could opt to:
    • Use a ComponentController.
    • Leave the ComponentController's default starting state as enabled.
    • Add the collider(s) to the ComponentController's components so its/their enabled/disable state(s) are synchronized/controlled.
    • Set the enabled state delay for each component entry on the ComponentController to like 1-5ms (i.e. wait until next frame)
    • Set the enabled state for each collider as disabled on the prefab itself so when it is first instantiated it won't interact with anything.

With the component controller approach, you wouldn't need to write any code to work around that issue. The component controller will automatically set the starting state for all of the components registered with it when instantiated. Since each one would have a delay on the enabled side, it would wait until the delay period specified and then set the enabled state...which by that time the Transform should have been updated to the position and rotation specified.

NoelStephensUnity avatar Jul 17 '25 20:07 NoelStephensUnity

Thanks for the quick response. Changing the Transform on the prefab itself to not be 0,0,0 is not something I had thought of! We can do that or leave a custom patch in InstantiateNetworkPrefab for our copy of Netcode. The other solutions would have unknown knockons potentially with how our code currently expects colliders to be enabled or not.

zachstronaut avatar Jul 17 '25 20:07 zachstronaut

Hmmm... would this fix my problem while not changing behavior for anybody else? Grabbing Transform values from networkPrefab?

internal NetworkObject InstantiateNetworkPrefab(GameObject networkPrefab, uint prefabGlobalObjectIdHash, Vector3? position, Quaternion? rotation)
{
    Vector3 defaultPosition = position ?? networkPrefab.transform.position;
    Quaternion defaultRotation = rotation ?? networkPrefab.transform.rotation;
    var networkObject = UnityEngine.Object.Instantiate(networkPrefab, defaultPosition, defaultRotation).GetComponent<NetworkObject>();
    // networkObject.transform.position = position ?? networkObject.transform.position;
    // networkObject.transform.rotation = rotation ?? networkObject.transform.rotation;
    networkObject.NetworkManagerOwner = NetworkManager;
    networkObject.PrefabGlobalObjectIdHash = prefabGlobalObjectIdHash;
    return networkObject;
}

zachstronaut avatar Jul 17 '25 20:07 zachstronaut