When using physics with NGO there are some over-all differences than the typical single player model.
This all boils down to: Who has authority?
The authority controls the Rigidbody while the non-authority instances should (under normal conditions and if using NetworkRigidBody) have their Rigidbody set to kinematic mode. As such, a non-authority instance applying a force to to a Rigidbody will result in nothing happening (i.e. forces are not applied).
Generally speaking, you would use a NetworkTransform to synchronize any changes to the transform (authority side) based on the changes to the RigidBody (authority side). So, non-authoritative NetworkObjects with a Rigidbody (kinematic), NetworkRigidbody, and NetworkTransform will basically just âmirrorâ the transform changes happening on the authoritative side.
Ownership vs Authority:
Since the NetworkTransform is really what synchronizes the changes to the transform by the Rigidbody on the authority side, authority boils down to whether the NetworkTransform is configured to be server or owner/client authoritative. When server authoritative, ownership plays no role in the NetworkTransform authority as that is based on how the NetworkManager was started (i.e. server-host vs client). When owner authoritative, ownership becomes the key element to determine who:
- Runs the physics simulation for the Rigidbody locally (i.e. non-kinematic)
- Synchronizes the non-authoritative instances with the changes to the transform
By changing the ownership, the authority changes⌠but there is an issue⌠what about existing forces before ownership changed? How does one keep those synchronized?
Synchronizing Forces & Velocities:
My disclaimer here is that NGO is not designed to handle deterministic physics and the default Unity physics is not deterministic. However, you can achieve a reasonable physics model that does synchronize an objectâs forces when ownership changesâŚand when you first start getting things âhooked upâ it can be easy to overlook some concepts.
RPC/Custom Messages vs NetworkVariable
Always try to remember that an RPC or custom named message should be treated like an âeventâ. This means they are messages that will be received and processed only by the currently connected (and synchronized) clients. This also means that the parameters of those messages are considered âtemporary valuesâ that while they could be applied to local properties of the connected clients will not be synchronized with any late joining client. Both RPC and custom messages are transmitted on the same (at the end) frame they were invoked/sent.
NetworkVariables are considered âpersistent state(s)â that both notify connected clients, that subscribe to the OnValueChanged notification, and synchronize late joining clients with the current state(s) during the connection process. Changes to NetworkVariables are sent on each new network tick. This makes the frequency in which NetworkVariables are updated tied to the NetworkManager.NetworkConfig.TickRate value (default is 30 times per second or every 0.033333ms)
So what does this all have to do with synchronizing forces?
Owner Authority Model And Collisions
You will find that the typical pattern is to handle collisions, on the authority side, with event message types since they are the fastest way to communicate that something has collided. However, if you think about âcollisionsâ and ask âwho has authority (on the Rigidbody)â you might not always need to communicate that something has collided. Of course, things get a bit more complicated when you start using an owner authoritative NetworkTransform as ownership changes and the Rigidbody changes (owner vs non-owner relative). You can then run into the conundrum of âWho reports a collision and who doesnâtâ?
Triggers donât play by the same rules as colliders since triggers are not dependent upon the Rigidbody being kinematic. As such, when using an âowner authoritativeâ approach you should consider designing your prefabs/objects with this in mind if you want to have owners of certain objects report a collision of other objects that are not owned. This type of scenario does require event notifications and a bit of âbrain noodling timeâ to make sure that when a local object instance should ignore triggers (i.e. the local object is owned by the local client) or colliders (i.e. the local object is not owned by the local client). Be cautious here, as you can quickly get into race conditions if you are always trying to handle both trigger notifications and collision notificationsâŚ
As an example:
A missile that is configured for an owner authoritative model and belongs to a remote client (i.e. not the host or server) could most likely need to have the server detect âcollisionsâ via triggers in order to send the âmissile explodeâ event to all of the clients (if you wanted the server to be the âauthority of missile impactsâ).
The reason why I bring up collision handling is that often you want the collision of one body into another body to apply a force⌠but sometimes the two bodies arenât both owned by the local client and so you need to determine âwho has the âultimateâ authority over a collisionâ? If you have an object-a owned by client-a and an object-b owned by client-b, who handles applying the force? This gets a bit complicated to explain, but the general idea here is you need to come up with a list of object types and the priority of collision handling (i.e. an enumerator property value or a tag applied to the GameObject) and then handle applying the resultant forces to both bodies. Of course, when you think about this scenario if you imagine the local object-a on client-a has âcollision priorityâ then it would apply the force to its local object-a (which the result of this is synchronized every 0.03333ms via NetworkTransform) and then send an event (RPC or custom message) that targets the owner of object-b with some form of collision information.
So, that is applying forces⌠but synchronizing forces in the event ownership changes (the real reason to synchronize forces) can get slightly tricky and really depends upon what kind of force is being applied (i.e. its duration)⌠as it might only need you to synchronize the Rigidbodyâs velocity and angular velocity values. This is where NetworkVariables can help you. You can subscribe to the NetworkManager.NetworkTickSystem.Tick in order to be notified when a new network tick takes place, and you can update your Rigidbodyâs velocities and/or forces during this timeâŚand âfor the most partâ every non-authoritative instance will be roughly (with an owner authoritative model) 1 network tick + half RTT (server to client relative) latent in getting updated.
Not to further confuse anyone, but you also need to consider âwhen is a good time to change authorityâ and âIs a TickRate of 30 going to be sufficient for your projectâs needsâ?
When using NetworkVariables with an owner authoritative model you need to make sure they are set with everyone read and owner write permissions⌠and depending upon your needs that might be the right configurationâŚof course you can consider client to server latencies and decide that updating the server, via an event message, with a Rigidbodyâs velocities and/or forces yields a better result as the ownership change actually emits from the server-side⌠meaning the server could be updated (by the current owner) with more current Rigidbody forces & velocities if owners sent those updates via a ServerRpc and upon receiving that ServerRpc the server updates the objectâs NetworkVariablesâŚwhich then keep all clients up to date with the âmost possible currentâ values.
Finally, there is always the very likely possibility that you will have the edge case scenarios where the server determines ownership should change in between ticksâŚwhich you could determine âwhat is an acceptable period of time to pass before the ownership changed message is propagated out?â. You can use the NetworkManager.NetworkTickSystem.Tick event to âtick synchronizeâ ownership changes which will assure the values updated are going to be as close as you can get.
But that wonât always make for a âsmooth visual transitionâ⌠to spare you the extended version of why you most likely will want to separate âvisualâ from the ânetwork synchronizedâ to achieve this⌠I went ahead and attached a project that gives you an example of how you can change ownership and keep the visuals of that change in ownership âlooking rightâ. It is definitely an opinionated example, but the general foundation concepts are in place.
The semi-transparent purple ball is the NetworkObject and the checkered ball is the âvisual representationâ of that NetworkObject. The ball follows the owner clientâs player, so in the image above you see the client (
yellow and right side image) leads the host since the client has authority.
When looking at the two images, the motion vector of the NetworkObject points from the leftâish bottom corner of the images to the topâish right corner of the images. You should also note that the NetworkObject (
semi-transparent purple) leads the visual for both the host and the client by about the same distance.
Hitting the space bar on the host side changes ownership between clients and rolls back to the host when it is owned by the last client connected.
Let me know if the information and attached example helps anyone?
9261783â1295883âOwnershipChangeOwnerAuthority.zip (23.7 KB)