Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Shared ownership and NetworkTransform

Discussion in 'Netcode for GameObjects' started by mishakozlov74, Nov 16, 2023.

  1. mishakozlov74

    mishakozlov74

    Joined:
    Aug 8, 2018
    Posts:
    140
    Hello there!

    I'm working on a co-op game based on physics interactions, and one crucial aspect involves players carrying an object together.

    I've implemented client-authoritative movement (cheating isn't an issue for us). However, the challenge arises because both players influence the carried object simultaneously. Assigning ownership to just one player isn't feasible; it needs to be "shared" since they move together.

    The approach I've considered involves using joints. I've added two joints for players, and each client simulates the local movement of the carried object to mask network lag. While this method shows promise, I've encountered a problem I've dubbed "the drifting problem." Despite tweaking ConfigurableJoints extensively, I haven't found a solution to the drifting issue. Perhaps I'm using the wrong joint type or missing a specific setting?

    Alternatively, I'm contemplating disabling physics entirely when the second player picks up the object and calculating movement using mathematical calculations. This seems simpler, although I haven't tested it yet.

    If anyone has implemented a similar feature or has insights on how to achieve this or can pinpoint where I might be going wrong, I'd greatly appreciate your wisdom. I'm feeling quite exhausted dealing with this mechanic...

    Thanks, Mike.
     
  2. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    244
    Hey Mike!
    :D

    So... I have actually been asked this question before and it is indeed a puzzler since you are wanting to synchronize the motion of an object that 2 (or more) players might be interacting with. The only thing I could think of that "may work" is to sort of "invert" the grabbing action where when a player "grabs" the object the player is parented under the object (as opposed to the more logical approach by thinking you want to parent it under a player or just try to keep a local reference and keep everything synchronized...which will be very difficult).

    The idea would be similar to this modification I made to the bite-size client driven sample that was a quick-hack for some other users during the Dev-Blitz that wanted to see an example of how to get owner authoritative player NetworkTransforms to work with a server authoritative moving platform.

    So, here is the logic I have swimming around in my head (it is end of week so...there might be a gotcha in this line of thinking o_O):

    Grabbing The Object:
    Like the modified client driven sample (when a player enters a trigger it gets parented) you could replace that with when a player interacts with the object...interacting "grabs" the object (player is parented under the object and NetworkTransform of the player is set to be in local space)...interacting again lets go of the object (player is no longer parented and the NetworkTransform of the player is set to be in world space).
    That is the easy part...;)

    The Influencer Layer:
    When a player grabs the object and is parented under the object, you will want the "over-all" movement of the object to have some kind of influence on each player. In order to do this, your object should have a series of collision boxes (rectangles most likely) that form a "fence" around it such that the colliders are spaced far enough away from the object that players who come up to grab the box won't be clipping into the fence...but close enough that if the object moves they will get "pushed" by the fence. Each of these colliders will have a "special layer" assigned to them (let's call it the "Influencer" layer). When a player is not parented under the object, the player's collider will exclude the Influencer layer and when the player is parented under the object it will include the Influencer layer (i.e. will get pushed by it if it moves with the object and the player isn't moving or is moving in the wrong way and the over-all force of the players grabbing the object overwhelms the not very cooperative player...more on the players' force next).

    Applying Force:
    Players that are "grabbing" the object would need to regularly report/send some form of "relative force" (perhaps based on a specific distance from the center of the object or a constant value...or a value based on character selection...etc).
    This could be done one of two ways:
    1. Via regular Rpc messages
    2. Via a NetworkVariable on a NetworkBehaviour component on the player
    I would recommend the 2nd of the two since it will get synchronized on tick intervals (the same time NetworkTransforms are updated). Rpcs could be used, but since this is a physics dependent scenario that requires synchronizing multiple forces it might work out better to use tick synchronized NetworkVariables. If you absolutely feel like you need to do this with Rpcs then you would want to store the values off to be applied later (i.e. not when the Rpc was invoked) since messages are applied during the EarlyUpdate that happens prior to FixedUpdate (you can sometimes run into scenarios that values applied to a Rigidbody prior to FixedUpdate on the same frame can be have weird results since the physics simulation hasn't run for the frame).

    Whether using Rpcs or NetworkVariables, you don't want to apply any values to a Rigidbody when notified of a change in force (i.e. during the EarlyUpdate). You will want to apply the force values during the object's FixedUpdate or Update method (this you will likely have to fiddle with to see which provides the best result).

    Bringing It Together:
    So, now we have one or more players parented under the object, the players will collide with the "Influencer Fence", and each player is updating an owner write everyone read NetworkVariable that will contain the current force vector being applied by the player to the object. Now it is time to "move" the object based on the players' applied forces.

    Since a server-host is really the "hub" to all messages for all players, I would make the server-host the owner of the object so you know that when you are updating the object you are working with the "most current" (as latency permits) state of the players and you have a single source of truth (server-host) of how the object will move.

    During the FixedUpdate or Update of the object being "grabbed", you would get an average of the "child players" forces and then apply that average force (or velocity...whichever is easiest in the end) to the object. You will also want to store the object's "average force/velocity" in a server write everyone read NetworkVariable which players will use on their end when applying force/velocity to their Rigidbody (i.e. dot product could be useful here where object <dot> player normalized velocity/force could yield how much of the current frame's player motion/force you want to apply to the player...anything less than zero you could opt to not apply that frame in order to clamp any player's inverted motion relative to the object's...or possibly use the inverted force...whichever yields the best result).

    As the object moves the players should move in a "relatively" similar direction since they are parented under the object. If a player is not "going the right way" but the rest of the players are moving in a "generally similar direction"...then the "Influencer Fence" will push the stray player in the right direction to help keep all players "grabbing the object" within a visually reasonable distance from the object itself. If a player is "standing still", then the player should "move with" the object's motion since the player is parented under the object. When applying motion to the players, you will always want to take the object's motion into account to help "influence the players motion" relative to the object.

    I think something along these lines would yield the "smoothest" over-all end result (the feel of the motion and visually synchronizing the motion).

    So...if I were to try and tackle this kind of feature...something like the above is what I would try first.

    Let me know if this helps you with your project... sounds like fun! :)
     
    Last edited: Nov 18, 2023
    mishakozlov74 likes this.
  3. mishakozlov74

    mishakozlov74

    Joined:
    Aug 8, 2018
    Posts:
    140
    Gosh, once again I can't stress enough how I appreciate your detailed answers! I'll try to wrap my head around it and implement it!

    Sounds very promising!
     
  4. NoelStephens_Unity

    NoelStephens_Unity

    Unity Technologies

    Joined:
    Feb 12, 2022
    Posts:
    244
    No worries... right now I am working on some things that requires a chunk of my time...otherwise I might have tried to get a working sample for you.
    Let me know how it goes and if you run into any issue with this approach!
     
    mishakozlov74 likes this.