Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

Question Networked gameobject jumps back to original position on client

Discussion in 'Netcode for GameObjects' started by Bender_R, Jan 24, 2024.

  1. Bender_R

    Bender_R

    Joined:
    May 21, 2018
    Posts:
    115
    Hi there,

    I have a gun in my project, with among others:
    - Network Object
    - Network Transform Client script (setting OnIsServerAuthoritative return false)
    - Network Rigidbody
    attached to it.

    Grabbing the gun as host or client works fine. Syncing works well. Movement and rotation are synced well.

    Only thing that happens is that if from client the gun is grabbed (using XR Rig) and at some point is released, the gun flies back to the position where I grabbed it.

    Any ideas what's causing this?

    Cheers!
    - Bender
     
  2. bugfinders

    bugfinders

    Joined:
    Jul 5, 2018
    Posts:
    1,934
    show us how we can recreate this in our own project
     
  3. Bender_R

    Bender_R

    Joined:
    May 21, 2018
    Posts:
    115
    Actually as I described. With a random gameobject like a cube, with attached scripts and an XR Rig.
     
  4. bugfinders

    bugfinders

    Joined:
    Jul 5, 2018
    Posts:
    1,934
    what scripts?
     
  5. Bender_R

    Bender_R

    Joined:
    May 21, 2018
    Posts:
    115
    - Network Object
    - Network Transform Client script (setting OnIsServerAuthoritative return false)
    - Network Rigidbody
     
  6. bugfinders

    bugfinders

    Joined:
    Jul 5, 2018
    Posts:
    1,934
    well "Grabbing the gun as host or client works fine. Syncing works well. Movement and rotation are synced well.

    Only thing that happens is that if from client the gun is grabbed (using XR Rig) and at some point is released, the gun flies back to the position where I grabbed it." doesnt happen by voodoo

    so, if you arent going to share enough to recreate.. I'll leave you to your misery
     
  7. Bender_R

    Bender_R

    Joined:
    May 21, 2018
    Posts:
    115
    What do you need to recreate than? Just looking for someone who recognizes te problem. I'm not able to share whole project. Do you need a screenshot of the gameobject?
     
  8. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    265
    What does your parenting code look like?
    This sounds like either:
    • You are NetworkObject parenting and de-parenting with world position stays set to false
      • Just assure it is deparenting with world position stays set to true.
    • You are just changing the parent of a child GameObject nested under the weapon's NetworkObject/GameObject(i.e. NetworkObject stays in its original location while the child is parented under the player's GameObject hierarchy) and reparenting it under the original weapon's NetworkObject's GameObject (or some child under it) causes the visual anomaly you are seeing.
    Without having more details on how it is parented, the NetworkTransform settings, and how it is being "de-parented" it will be hard to determine the issue specifically.
     
  9. Bender_R

    Bender_R

    Joined:
    May 21, 2018
    Posts:
    115
    Hi Noel,

    Thanks for your response. I don't do anything special for parneting. Just using the XR Rig with XR Grab Interactable. Here some screenshots:

    Hand: https://ibb.co/Db03jfQ

    Gun: https://ibb.co/3WgYDvP

    Network Manager: https://ibb.co/FKkrbjv

    (sorry, for some reason uploading image doesn't work)

    Thanks for you help!

    - Bender
     
  10. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    265
    Could you provide snippets of the code/script that is parenting and deparenting the objects or are you using the XR Grab directly?
     
  11. Bender_R

    Bender_R

    Joined:
    May 21, 2018
    Posts:
    115
    I use XR Grab directly.

    Here the issue:


    As mentioned before, this is from client. When I switch to host, it is working well and position is synced to client.
     
  12. Nyphur

    Nyphur

    Joined:
    Jan 29, 2016
    Posts:
    98
    This happens because the server owns the object and has authority over its position, the client actually never has authority to move it. XR Grab is setting the position on the client each frame so that it appears to be in the hand for that specific client, but it's not actually moving on the server so as soon as you stop setting its position on the client the server re-asserts the original position via the NetworkTransform. Also no other clients will see it in that client's hand.

    It works when you're host because you're both client and server and you are the object owner, so you can set its position. The solution is that you need to change the NetworkTransform to a ClientNetworkTransform, re-parent the object to the player when picked up and de-parent when dropped, and change the owner of the object when picked up and dropped. You'll still run into other visual sync issues if doing this though, see this thread for more info on a more robust solution: https://forum.unity.com/threads/player-hierarchical-networkobjects.1207012/#post-8951985

    This is not something you can achieve with pre-built components, if you want to make a multiplayer game you will need to write your own code for parenting, changing ownership of items, sending info back and forth between client and server, etc.
     
  13. Bender_R

    Bender_R

    Joined:
    May 21, 2018
    Posts:
    115
    Thanks for your reply on this. I'm a bit confused about the complexity of something trivial as grabbing an object.

    I used to use PUN2 for multiplayer, but since deprecated I'm looking for a new framework. In PUN2 you could easily add a one line script to request ownership when grabbing an object from a client.

    Doesn't have NGO a similar way for the simple grab and move actions?

    Cheers!
    -Bender
     
  14. Nyphur

    Nyphur

    Joined:
    Jan 29, 2016
    Posts:
    98
    Yes, NGO has the ChangeOwnership method for changing ownership and the NetworkTransform script for syncing transforms. But you have to handle the changing ownership and moving transforms etc yourself, NGO is very barebones and there's no standard generalised solution for something like an item pickup system built into it.

    I'll be the first to agree with you that there are loads of very obvious and common use cases that are needlessly complex in NGO. The Unity devs don't make games themselves so they don't implement any of the common use cases and the samples are often very far from what an actual game needs. Once you get used to it though, you can build your own systems for everything and it's fine.
     
  15. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    265
    This kind of issue is completely resolved in the near future distributed authority update.
    Basically there will be either a new type of helper component provided or NetworkRigidbody will be updated to handle this.

    Parenting with Rigidbodies will look like:
    • Change ownership
    • Parent object (TryParent)
      • Position if needed
    • Add a FixedJoint to the child (this all might be "automatic and configurable")
      • Assign the parent's Rigidbody to the FixedJoint
    Done.

    Deparenting will look like:
    • On the child:
      • Remove parent (TryRemoveParent)
      • Destroy the FixedJoint
      • Add a force or velocity to the object (optional)
    Done.


    For non-Rigidbody scenarios:

    Parenting:
    • Change ownership
    • Parent
    Done.

    Deparenting:
    • Remove parent
    Done.

    For now, I would get things working the best you can if you are not planning to release in the next 3'ish month (or so) and then check out the updates when the distributed authority version is released. Many things relating to visual synchronization and such have been improved greatly.

    If you are planning on releasing relatively soon, ping me back and I will post a sample project here that you can use as a reference to get it working in v1.8.0/v1.7.1.
     
  16. Nyphur

    Nyphur

    Joined:
    Jan 29, 2016
    Posts:
    98
    Improvements to the authority system sound good, but calling this completely solved sounds overly optimistic. Games don't just parent picked up objects to the player's root and call it a day. Any system with items being picked up by players is going to be more complex than this, as games tend to put items in specific locations like the player's hand bones and that's not achievable in NGO through just parenting.

    On the topic of ownership, there is currently no valid authority configuration for a networkrigidbody / networktransform on a picked up item. No matter who you give ownership of the item and authority over the networkrigidbody or networktransform, only that one player will ever see the object where it's supposed to be. All other players and the server will see it lagging behind or out of sync with animations.

    The only valid solution I've found is to stop syncing the networktransform while the item is picked up and have each client set its own position for the object or activate a constraint. If the new system can be configured to automatically do that, that would be pretty awesome. Is that what this is?
     
  17. Bender_R

    Bender_R

    Joined:
    May 21, 2018
    Posts:
    115
    Ok, thanks a lot for this update. Looking forward having this simplified. So, release somewhere in 3 months?
     
  18. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    265
    Actually, what I was talking about was if you have two NetworkObjects and you want to parent one under the other and especially if both have a Rigidbody component (i.e. get into a jeep and have fellow players jump in with you and drive around).

    The issue you are describing, regarding the bone location of the object, can be accomplished with minimal script and just a little bit of preparation.

    The general tendency is to lump everything on the root GameObject of a prefab, but for the scenario you are describing you should divide up your prefab where the root GameObject is really just a "container" for the actual object to be held.

    As an example, you might want to be able to freely move an item around when it is not picked up (i.e. could be kicked, etc). In this case the root of your prefab should only have a NetworkObject and NetworkTransform:
    • Root "ObjectToHoldParent" (GameObject)
      • Components:
        • NetworkObject
        • NetworkTransform
    However, when the item is picked up, you want to be able to migrate certain parts of that prefab to a specific location on a model (i.e. like a player) while also keeping the ability for those parts to have NetworkBehaviours that still function correctly (i.e. RPCs, NetworkVariables, Netcode events, etc).

    With this in mind you would want all of those parts to be nested under the root GameObject on a child:
    • Root "ObjectToHoldParent" (GameObject)
    • Components:
      • NetworkObject
      • NetworkTransform
    • ObjectToHold (Child GameObject)
    • Components:
      • AutoParentChildNetworkBehaviour (** see below)
      • NetworkAnimator (just for example purposes, not required)
      • CustomObjectNetworkBehaviour (object's custom behaviours)
      • etc.
    The key to "easy parenting" is all contained within the child ObjectToHold's AutoParentChildNetworkBehaviour. Here is a partially functional (the parenting part) pseudo code example of how to use the above prefab layout and get every client (late join or connected) to keep synchronized with the parented child object and have the parented child object move smoothly with the player's "holding node" (i.e. hand or the like):
    Code (CSharp):
    1.  
    2. public class PlayerLogicBehaviour : NetworkBehaviour
    3. {
    4.     // Assigned in-editor to the child node on the player prefab where you want
    5.     // things to be held relative to the player.
    6.     public GameObject ObjectHoldNode;
    7.  
    8.     // The rest of your player logic
    9. }
    10.  
    11. // This should be placed on the child object (i.e. ObjectToHold) that has the visuals and such
    12. // that really represent the "object being held".
    13. public class AutoParentChildNetworkBehaviour : NetworkBehaviour
    14. {
    15.     private Vector3 m_ObjectPositionBeforeParenting;
    16.     private Transform m_OriginalParent;
    17.     private void Awake()
    18.     {
    19.         // Preserve the original parent and local space position relative to the parent.
    20.         // All client instances will initialize these to the same prefab preset values.
    21.         m_OriginalParent = transform.parent;
    22.         m_ObjectPositionBeforeParenting = transform.localPosition;
    23.     }
    24.     public override void OnNetworkDespawn()
    25.     {
    26.         // Assure it is "re-attached" to the original transform parent and has the same position,
    27.         // in the event it is being used in an object pool, when despawned.
    28.         transform.SetParent(m_OriginalParent, false);
    29.         transform.localPosition = m_ObjectPositionBeforeParenting;
    30.         base.OnNetworkDespawn();
    31.     }
    32.  
    33.     // This is automatically invoked on all clients when a NetworkObject's parent changes
    34.     public override void OnNetworkObjectParentChanged(NetworkObject parentNetworkObject)
    35.     {
    36.         // If a parent is assigned...
    37.         if (parentNetworkObject != null)
    38.         {
    39.             var playerLogicBehaviour = parentNetworkObject.GetComponent<PlayerLogicBehaviour>();
    40.             // If a player picked it up
    41.             if (playerLogicBehaviour != null)
    42.             {
    43.                 // Set the parent to the player's assigned ObjectHoldNode's transform
    44.                 transform.SetParent(playerLogicBehaviour.ObjectHoldNode.transform);
    45.                 // Zero out the local space position so the held object moves and rotates with
    46.                 // the player's  ObjectHoldNode's transform
    47.                 transform.localPosition = Vector3.zero;
    48.             }
    49.         }
    50.         else // Otherwise, a parent was removed...
    51.         {
    52.             // The item was dropped/removed. Put the child back under its original transform and
    53.             // apply any pre-set local space offset.
    54.             transform.SetParent(m_OriginalParent);
    55.             transform.localPosition = m_ObjectPositionBeforeParenting;
    56.         }
    57.         base.OnNetworkObjectParentChanged(parentNetworkObject);
    58.     }
    59. }
    60.  

    So, as long as the "ObjectToHoldParent" is spawned, it doesn't really matter where you place its children as they have already been associated with the ObjectToHoldParent's NetworkObject. So really all you need to know about is when the child's parent NetworkObject has been parented or removed from a parent in order to handle the automatic parenting of the child object. The code is sever-client agnostic and should be updated on connected and late joining clients automatically.

    This approach works fine in NGO today. The only area where you might want to possibly smooth up is the picking up or dropping transition... which you can modify the above to handle lerping to the target parent position (use a coroutine or the like) of the ObjectToHold... but that is more specific to your project's visual needs/goals in regards to picking things up and dropping them.

    The above approach works very well for objects that "move with" a player or other object (typically based on animation and/or motion). However, when you start getting a mix of NetworkObjects with Rigidbodies that all could be parented under a single NetworkObject with a Rigidbody (i.e. a bunch of players board a large vehicle like a ship or spaceship and the players can all move around and interact with things on the moving vehcile)...then it can become complex with NGO as it is today.

    Side note:
    Just worth mentioning that the ObjectToHold and any children of the ObjectToHold do not require a NetworkTransform to synchronize the motion. The player object and the motion (i.e. due to animations) of the ObjectHoldNode will automatically move the parented ObjectToHold.


     
    Last edited: Feb 3, 2024
  19. Nyphur

    Nyphur

    Joined:
    Jan 29, 2016
    Posts:
    98
    This is a great solution, I hope it helps some people!

    It's similar to the solution I've been posting except mine doesn't require a specific prefab structure for picked up items. In your solution the child object has no sync issues because it has no NetworkTransform and its position is managed by a script while being held. My solution was to simply switch off the NetworkTransform while the item is held so the position stops syncing, which achieves the same result without the need for a new prefab structure. It feels like a hack but it's a more flexible solution.

    The main point was that NGO has no built-in solutions for these things and it's left up to the developer to create their own systems for even the most obvious and standard use cases. The sample projects contain quite a few examples where the solutions don't match an actual game use case but some simplified version of it. There's a reason that so many threads have popped up on this forum over and over again about issues with picking up items.
     
  20. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    265
    Yeah, I hear you on this (for sure) and we definitely need to start populating the Unity.Netcode.Components namespace with things like the above example. There were some recent PRs to update all of the NetworkVariable container issues (you now have dictionaries and such) as well as PR-2820 that provides some additional functionality you might find of interest.

    However, like you mentioned we need to provide more examples (like above) as companion components to the SDK... but readily available within the SDK (i.e. you don't need to hunt down/install another package or the like). This is something we are looking to improve in the near future after we finish some foundational updates.
     
  21. Bender_R

    Bender_R

    Joined:
    May 21, 2018
    Posts:
    115
    Hi guys,

    Good to read, but you somewhat lost me here. I think the issue I have should be easier to fix (at least I hope...)

    Since the issue is in the ownership, I created a NetworkGrabInteractable, like this:

    Code (CSharp):
    1. public class NetworkGrabInteractable : XRGrabInteractable
    2. {
    3.     private NetworkObject networkObject;
    4.  
    5.     protected override void Awake()
    6.     {
    7.         base.Awake();
    8.         networkObject = GetComponent<NetworkObject>();
    9.     }
    10.  
    11.     protected override void OnSelectEntered(SelectEnterEventArgs args)
    12.     {
    13.         base.OnSelectEntered(args);
    14.            
    15.         RequestOwnershipServerRpc(NetworkManager.Singleton.LocalClientId);
    16.        
    17.     }
    18.  
    19.     protected override void OnSelectExited(SelectExitEventArgs args)
    20.     {
    21.         base.OnSelectExited(args);
    22.            
    23.         RemoveOwnershipServerRpc(NetworkManager.Singleton.LocalClientId);
    24.  
    25.     }
    26.  
    27.     [Rpc(SendTo.Server)]
    28.     void RequestOwnershipServerRpc(ulong clientId, ServerRpcParams rpcParams = default)
    29.     {
    30.         if (networkObject.OwnerClientId == clientId)
    31.         {
    32.             return;
    33.         }
    34.  
    35.         networkObject.ChangeOwnership(clientId);
    36.     }
    37.  
    38.     [Rpc(SendTo.Server)]
    39.     void RemoveOwnershipServerRpc(ulong clientId, ServerRpcParams rpcParams = default)
    40.     {
    41.         if (networkObject.OwnerClientId != clientId)
    42.         {
    43.             return;
    44.         }
    45.     }
    46. }
    Unfortunately for some reason I still don't seem to get the ownership.

    Is this a construction that should work, or is this really more difficult?

    Cheers!
    - Bender
     
  22. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    265
    There were a few minor adjustments I made:
    • UniversalRpcs should end with Rpc (using ServerRpc will conflict with the legacy ServerRpc)
    • You don't need to send the identifiers who is sending (that is handled for you in the RpcParams)
    • When dealing with ownership things, it is recommended setting the RequireOwnership to false in the attribute decoration (otherwise it won't be processed if the sender is not the owner).
    Either case,
    Here are the adjustments made:

    Code (CSharp):
    1. public class NetworkGrabInteractable : XRGrabInteractable
    2. {
    3.     private NetworkObject networkObject;
    4.  
    5.     protected override void Awake()
    6.     {
    7.         base.Awake();
    8.         networkObject = GetComponent<NetworkObject>();
    9.     }
    10.  
    11.     protected override void OnSelectEntered(SelectEnterEventArgs args)
    12.     {
    13.         base.OnSelectEntered(args);
    14.  
    15.         RequestOwnershipRpc();
    16.     }
    17.  
    18.     protected override void OnSelectExited(SelectExitEventArgs args)
    19.     {
    20.         base.OnSelectExited(args);
    21.  
    22.         RemoveOwnershipRpc();
    23.  
    24.     }
    25.  
    26.     [Rpc(SendTo.Server, RequireOwnership = false)]
    27.     private void RequestOwnershipRpc(ServerRpcParams rpcParams = default)
    28.     {
    29.         if (networkObject.OwnerClientId == rpcParams.Receive.SenderClientId)
    30.         {
    31.             return;
    32.         }
    33.  
    34.         networkObject.ChangeOwnership(rpcParams.Receive.SenderClientId);
    35.     }
    36.  
    37.     [Rpc(SendTo.Server, RequireOwnership = false)]
    38.     private void RemoveOwnershipRpc(ServerRpcParams rpcParams = default)
    39.     {
    40.         if (networkObject.OwnerClientId != rpcParams.Receive.SenderClientId)
    41.         {
    42.             return;
    43.         }
    44.  
    45.         if (!networkObject.TryRemoveParent())
    46.         {
    47.             Debug.LogWarning("Failed to remove parent");
    48.         }
    49.     }
    50. }
    Let me know if this resolves your issue?
     
    Last edited: Feb 7, 2024
  23. Nyphur

    Nyphur

    Joined:
    Jan 29, 2016
    Posts:
    98
    NetworkVariable dictionaries, you may have just made my day!
     
  24. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    265
    PR-2813
    • Added: NetworkVariable now includes built-in support for NativeHashSet, NativeHashMap, List, HashSet, and Dictionary
    • Added: NetworkVariable now includes delta compression for collection values (NativeList, NativeArray, NativeHashSet, NativeHashMap, List, HashSet, Dictionary, and FixedString types) to save bandwidth by only sending the values that changed. (Note: For NativeList, NativeArray, and List, this algorithm works differently than that used in NetworkList. This algorithm will use less bandwidth for "set" and "add" operations, but NetworkList is more bandwidth-efficient if you are performing frequent "insert" operations.)
    ;)
     
    Nyphur, Bender_R and bugfinders like this.
  25. bugfinders

    bugfinders

    Joined:
    Jul 5, 2018
    Posts:
    1,934
  26. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    265
    You can thank @Kitty-Unity for that PR... she has been busy indeed!
     
    Nyphur and bugfinders like this.
  27. Bender_R

    Bender_R

    Joined:
    May 21, 2018
    Posts:
    115
    Thanks. This runs like I would expect. But I then run into the issue of the re-parenting. Code raises the "Failed to remove parent". Do I understand that from this point I should create the custom code for re-parenting? Is it maybe possible to share a piece of code how to do the grab with a standard xr rig grabbing a cube from client and server?

    Still a bit confused about this. I'm currently also trying the Alteruna Multiplayer solution and this simple framework does handle grab seamlessly.
     
    Last edited: Feb 9, 2024
  28. Bender_R

    Bender_R

    Joined:
    May 21, 2018
    Posts:
    115
    @NoelStephens_Unity As there's no further reply, can I conclude that there's no easy solution for grabbing using the XR Interaction Toolkit with NGO? Is this something that's on the roadmap?
     
  29. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    265
    Apologies for not responding sooner.
    I have been working on a new "session mode" for NGO (distributed authority) which would indeed provide an easier way to handle this, but that isn't an option at this time. My intention is to put together an XR sample with as simple of an implementation as possible to grab and drop objects... of course that is my intention...:(

    Depending upon how the day shapes up for me, I might get an hour to churn a small example project out... if not maybe over the weekend I can make it my "personal project" (typically I do one or two code related things if I have the time).
     
  30. Bender_R

    Bender_R

    Joined:
    May 21, 2018
    Posts:
    115
    Ah, thanks a lot! A sample project would be great. But.. weekend is important to relax! Enjoy! I'll see it coming anywhere upcoming weeks..