Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Ok, seriously, how are you supposed to code an Item Pickup and Drop system with Netcode?????

Discussion in 'Netcode for GameObjects' started by SatiricalSnake, Feb 10, 2023.

  1. SatiricalSnake

    SatiricalSnake

    Joined:
    Jun 10, 2019
    Posts:
    2
    I have been at this for months with no real progress. All I want to happen is for a client to walk over to a NetworkObject and have it parented to their hand when they pick it up. I would much like to avoid a huge chain of nested NetworkObjects reaching all the way to the hand.

    For context, my Items derive from an abstract Item class and use prefabs. I also use ScriptableObjects for them, as usual.

    I started to make *some* progress when I figured out I could parent a NetworkObject with ParentConstaint assigned to the hand while being actually parented to the Player root. This worked flawlessly...on the server. It doesn't work in the slightest for Clients.

    I also tried to do something like making the NetworkObject just an empty, spawn it, and have the NetworkObject instantiate my items (now MonoBehaviours) as children. This did not really work that well for a couple of reasons.

    Why is something so minuscule in a single-player game, becoming the biggest obstacle for my first multiplayer game?

    What does Unity say the best practice is for this? There are next to no tutorials about this online. Can anyone help me?
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,533
    Just from the top of my head a thought: why do you think the hand-held object needs to be network synchronized at all?

    I see it this way: item gets picked up by walking over it. In reality, that item gets removed from the world. A message is sent around with the info „player holds item x in his hand“. Every client now enables or spawns a local (!) object of this kind to that player‘s hand and synchronizes its position every frame with that player‘s hand position.

    Since the players and their animations are presumably already network synchronized you don‘t actually have to also synchronize the carried item in a player‘s hand „again“ because its position can be safely derived from the already synchronized player position and animation state. In other words, trying to synchronize an attached object wouldn‘t actually work any better, in fact, it might even cause the item not to be in perfect synch with the animation due to possible delays.
     
  3. MrBigly

    MrBigly

    Joined:
    Oct 30, 2017
    Posts:
    221
    Perhaps you can suspend replication of the asset's state while it is a child?

    I honestly don't know how that would be done with NfGO. Perhaps you can disable the NetworkXXX component attached to it?
     
  4. RikuTheFuffs-U

    RikuTheFuffs-U

    Unity Technologies

    Joined:
    Feb 20, 2020
    Posts:
    440
    Hey @SatiricalSnake , have you already had a look at the examples in the official ClientDriven sample?

    If not, you can download it here.

    Its examples show how to use the third person controller asset in a multiplayer environment for movement and picking objects up, which seems to be what you're looking for.
     
  5. Serinx

    Serinx

    Joined:
    Mar 31, 2014
    Posts:
    788
    I struggled with the same thing. The Unity example only works for parenting to the root object, but if you want to parent to the hand (or any other child object) I can't find any official advice.

    CodeMonkey recently did a multiplayer video and he hit this issue. He got around it by not parenting the object to a child transform, but rather having the object follow a target transform. He wasn't using armatures so I don't know if this approach would work for hands very well.

    My solution was to basically do what CodeSmile said -
    1. An item is on the ground, available to be picked up
    2. The player asks the server if they can pick up
    3. If allowed, the server tells all the clients to spawn a visual representation of the picked-up item and parents it to the hand (this is NOT a network object)
    4. The server hides the object on the ground

    If the player drops the item, it asks the server to unhide the pickup item and tells all clients to destroy the visual representation.

    If the player throws the item, the visual representation is destroyed and the server spawns an actual synchronized physics projectile at the defined location.

    This does mean I need 3 prefabs per item, but because each gameobject actually has different behaviour it sort of makes sense. The visual component is a prefab itself which is used by the other 2 prefabs :)

    You can see this in action in the video on my other post:
    https://forum.unity.com/threads/i-know-its-not-much-but-just-wanted-to-show-some-progress.1391377/

    Hopefully, this helps someone!
     
  6. MrBigly

    MrBigly

    Joined:
    Oct 30, 2017
    Posts:
    221

    Unity's best practice suggests you take a vehicle prefab, break it into several separate prefabs, spawn the pieces into the scene, then reassemble them back into the original vehicle prefab.

    I understand why they say to do this. I understand why you have this problem and your solution is necessary.

    Regardless, none of this is standard approach or practice to build a video game.
     
    Serinx likes this.
  7. Serinx

    Serinx

    Joined:
    Mar 31, 2014
    Posts:
    788
    @MrBigly I agree it's a very roundabout way of doing things. I hope Unity streamlines this soon.
     
  8. afavar

    afavar

    Joined:
    Jul 17, 2013
    Posts:
    66
    After trying several ways here is the simplest one IMO.

    You can use Scriptable Objects to represent the item data. In the SO, each item has an ID (uint/int). These SO's are referenced in a list at my game manager script. There is also a method to get the SO by the ID of it.

    When a player wants to pick an item, a server rpc is called and sets the currentItem NetworkVariable to the ID of the object and Despawns the object so others can't pick it as well. At that point, a player can pick up an item and everyone know the ID of the item the player has picked up.

    The last thing is the visual representation of the player's current item. This can be achieved by subscribing the currentItem NetworkVariable to listen changes. After the player picks up an item, the ID will be set by the server and every client will be notified. At that point, you can locally Instantiate(no need to spawn after instantiating since it will act as a replica) the Item and disable it's collider and rigidbody. (if there is any)

    P.S: I have used NetworkManager.Despawn() to destroy the object after it is picked up. Calling Destroy(gameObject) on the server only works with dynamically spawned objects but not with in scene placed objects.
     
    Last edited: Mar 14, 2023
    RustCoat_ and RikuTheFuffs like this.
  9. MrBigly

    MrBigly

    Joined:
    Oct 30, 2017
    Posts:
    221
    If NGO imposed any requirement to break apart a prefab into pieces just to work with NGO for the simple task of picking up the prefab IMO pretty much says NGO is not an ideal solution to be used in its current form. But I wonder if the wrong question is being raised?

    I don't know if there are other limitations by NGO that would prevent what I am going to share below, I haven't played with it and now more than ever I don't want to spend any time with it. But someone else may take this idea and try it out.

    It seems that the question being asked is how can NGO parent the weapon to the correct transform, the one associated with the hand?

    Perhaps the question should be how can NGO help support and defer to the application level (game logic) to parent the weapon to the hand's transform?

    To begin, instead of replicating the parent transform of the weapon, replicate the weapon's owner identifier.

    Write the code on the Authority to call into the weapon's OwnerId property with the Pawn's identifier. Replicate this property value. NGO should then set the Weapon's OwnerId property with the Pawn's identifier on each of the Clients.

    The setter on the weapon's OwnerId property should then call into the Pawn's TakeOwnershipOfWeapon(this) with itself as the only parameter. The Pawn knows the transform for the hand and can form the relationship. Since the setter is the same code on all ends of the wire, the same weapon should parent to the same hand on the same Pawn.

    Edit: To be clear...

    It seems to me that this is the correct way to parent an asset to a Pawn's hand (for example). I don't think the netcode layer should be used to determine the proper parent transform, nor should that be the expectation. If NGO supports the above strategy, then I would think NGO is good to go.
     
    Last edited: Mar 15, 2023
  10. hugokostic

    hugokostic

    Joined:
    Sep 23, 2017
    Posts:
    81


    I have made a video about the pickUp feature,
    server driven then passed to each player using Rpc and mono syncronisation, i'll provide my code on GitHub asap.
    my exemple is build using Client-Driven sample as base
     
  11. TheCrowXD

    TheCrowXD

    Joined:
    Jan 24, 2014
    Posts:
    5
    im facing the same problem. i tought that i would fix it in 5 minutes and 6 hours later im here.

    i dont get why something so "basic" for a videogame is so hard to accomplish with NGO
     
  12. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,462
    As a related issue, it doesn't seem that parenting works on prefabs that you spawn anyway. For example if you create a working Player Object prefab that spawns and functions fine, but then add a nested NetworkTransform component into that prefab somewhere on a child object then NGO fails to spawn it.

    The docs say this works already, so that's incorrect.

    I think if you plan on doing any kind of parenting then you should probably just sync the necessary variables in the parent class and push them down locally as needed. It does not seem to be stable enough to trust the built in parenting system right now.
     
  13. Trihkal

    Trihkal

    Joined:
    Apr 17, 2021
    Posts:
    2
    I’m super new at all this so I may be misunderstanding, but I think you may be looking for NetworkObject.TrySetParent().
    I have an Enemy spawner that starts in my game scene, and spawns the enemy prefabs.
    I use NetworkObject.TrySetParent() to child the enemies under the spawner.

    Like I said, super new but wanted to try and help if I could.