# Networked Physics
What's uuuuuuuuuuuuup. Unless you're casually perusing the docs, you're probably here because you put a
RealtimeTransform component on something and it's not working the way you expect. Well, you're in luck. This page is designed to explain how
RealtimeTransform works and some of the thinking necessary to get it to do what you want.
# How physics works in Unity
To really understand how
RealtimeTransform works, it's worth understanding how physics works in Unity.
Unity uses a physics engine behind the scenes from Nvidia called PhysX (get it, behind the scenes ;P). It works well, but it has one fatal flaw. It's non-deterministic. This means that if you set an object in motion on multiple devices, when it eventually comes to rest, it might not always be in the exact same place on all devices. Small bits of precision are lost each frame, and depending on the platform and compiler, your game might drift more or less.
For singleplayer games, this isn't a problem, but when it comes to multiplayer, it means that even with ideal starting conditions, multiple clients can come up with different answers to where an object is in space.
# That's cool, but why does that matter?
All it means is that Normcore needs to synchronize all objects between all clients all of the time. If physics were deterministic, all we would have to do is synchronize the initial conditions and each client would arrive at the same answer.
That said, if we have a few clients all simulating the same object, when they eventually drift, whose physics scene should we use as the source of truth? Who's scene is the correct one? Well, this is where the concept of ownership comes in.
# Ownership + Physics
To solve this problem,
RealtimeTransform introduces the concept of ownership. When an object is owned by a client, their simulation is considered the source of truth for that object.
RealtimeTransform synchronizes the state of the object from the owner to all other clients.
RealtimeTransform does this by looking at its owner. If the component is owned locally, it's going to synchronize the position, rotation, scale, velocity, angular velocity, and any other settings to the datastore. And if the
RealtimeTransform component is not owned locally, it's going to treat the values in the datastore as the source of truth and synchronize them to this object.
If you've added a
RealtimeTransform component to your game object and it keeps getting reset back to where it started, this is why. You're not the owner, and therefore the datastore values are treated as the source of truth.
At the end of the day, if you want to move an object, you need to request ownership of it.
Let's take a look at how this all fits together in practice.
# Using RealtimeTransform
Let's start in the editor and then once we understand the concepts, we'll move to code.
Create a fresh project, make a copy of the Blank Scene Template scene, and add a
Realtime component to the scene. Now, let's create a cube game object, move it up so it's above the ground, and throw a
RealtimeTransform component on it. Your scene should look like this:
Looks good! Now let's export a build to play around with. Go to File > Build Settings, export a build, and open it up. Now let's open the build and hit Play in the editor. Both the editor and the standalone build will show our cube. If we try to move it using the transform gizmo in the editor, we can see it jumps back to the start position. Let's try taking over ownership. Click the Request Ownership button on the
RealtimeTransform component in the editor. Remember, we're taking over ownership of the
RealtimeTransform, not the
RealtimeView. We'll get ownership instantly, and now we can use the gizmos in the editor to move the cube around:
Nice and easy!
It's worth noting that even though we are manipulating the world-space position and rotation of the object,
RealtimeTransform synchronizes the
localScale properties. This means that if you have multiple nested Transforms, you need to synchronize all of them with
Now if all you're doing is moving regular ol' game objects, you're good to go. Feel free to hop off the train and get to it! However, if you're using rigidbodies, then let's keep going and explain how physics works.
# RealtimeTransform + Physics
RealtimeTransform works a little differently when a Rigidbody component is present.
# Rigidbodies are synchronized in world space
The first change is that rather than synchronizing the
localRotation properties of the transform, it synchronizes the
rotation properties of the Rigidbody, which are in world space instead of local space. We do this because PhysX simulates all rigidbodies in world space. Moving a Rigidbody via the Transform local position and rotation properties instantly teleports the Rigidbody and does not perform collision detection.
# RealtimeTransform will request ownership when a collision occurs
Let's say you're holding a ball that has a Rigidbody and
RealtimeTransform component on it. If you throw it,
RealtimeTransform will automatically call
RequestOwnership() when it collides with another object, as long as the other object has a Rigidbody and
RealtimeTransform component on it, and it is not owned by anyone already. This will ensure that the collision works smoothly for local and remote players.
This also means that
RealtimeTransform will automatically call
ClearOwnership() when the Rigidbody comes to rest in order to allow other clients to take it over and simulate it.
If you're trying to directly move an object, make sure to mark it kinematic. Doing so lets PhysX know that you're controlling its position and it should not simulate the object directly. It also tells
RealtimeTransform that it should not clear ownership when the object goes to sleep.
However, if you're in a situation where you need to maintain ownership while sleeping, you can always set
Maintain Ownership While Sleeping in the Unity editor.
# RealtimeTransform + Rigidbody should not have parents
PhysX simulates all rigidbodies in world space. If you have a Rigidbody with a child that also has a Rigidbody component, you're going to end up with unpredictable results. The core issue is that when Unity synchronizes the Rigidbody
rotation of each object back to the Transform component, if it synchronizes the child before the parent, the child will end up at the wrong location.
This is further complicated when transforms are synchronized over the network. Normcore does not guarantee what order
RealtimeTransform components will be synchronized. Without Rigidbody components, this works fine because
RealtimeTransform synchronizes the local transform values. However, because we are forced to synchronize the world space position for rigidbodies, if a Rigidbody component is synchronized, and then its parent is synchronized, the position and rotation of the child Rigidbody will move and no longer be correct.
To summarize, rigidbodies cannot have parent game objects unless the parent never moves. That said, child game objects with
Collider components are perfectly fine as long as they do not have a Rigidbody component on them.