Sync Rigidbodies Over Network Using PUN 2
Syncing Objects in PUN 2 is simple, but what about syncing Rigidbodies?
Unlike regular GameObjects, Rigidbody is also affected by Gravity (if not Kinematic) and other objects as well. So instead of syncing just the object's Transform, we also need to sync a couple of additional parameters, such as velocity and angularVelocity.
In this post, I will show how to make interactive Rigidbodies that can be affected by every Player in the Room and synced over Network.
Unity version used in this tutorial: Unity 2018.3.0f2 (64-bit)
Part 1: Setting up PUN 2 and Multiplayer Example
We already have a tutorial on how to set up a multiplayer example using PUN 2, check the link below:
Make a multiplayer game in Unity 3D using PUN 2
Come back once you are done setting up a multiplayer project so we can continue.
Alternatively, you can save time by getting the source project from here.
Part 2: Adding Interactive Rigidbodies
If you followed the tutorial above you would now have 2 Scenes "GameLobby" and "GameLevel"
- Open the "GameLevel" Scene and create a couple of Cubes (GameObject -> 3D Object -> Cube)
- Add a Rigidbody component to each Cube
- Add a PhotonView component to each Cube
Now we need to create a new Script that will sync the Rigidbodies over the network.
- Create a new Script and call it PUN2_RigidbodySync
PUN2_RigidbodySync.cs
using UnityEngine;
using Photon.Pun;
public class PUN2_RigidbodySync : MonoBehaviourPun, IPunObservable
{
Rigidbody r;
Vector3 latestPos;
Quaternion latestRot;
Vector3 velocity;
Vector3 angularVelocity;
bool valuesReceived = false;
// Start is called before the first frame update
void Start()
{
r = GetComponent<Rigidbody>();
}
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.IsWriting)
{
//We own this player: send the others our data
stream.SendNext(transform.position);
stream.SendNext(transform.rotation);
stream.SendNext(r.velocity);
stream.SendNext(r.angularVelocity);
}
else
{
//Network player, receive data
latestPos = (Vector3)stream.ReceiveNext();
latestRot = (Quaternion)stream.ReceiveNext();
velocity = (Vector3)stream.ReceiveNext();
angularVelocity = (Vector3)stream.ReceiveNext();
valuesReceived = true;
}
}
// Update is called once per frame
void Update()
{
if (!photonView.IsMine && valuesReceived)
{
//Update Object position and Rigidbody parameters
transform.position = Vector3.Lerp(transform.position, latestPos, Time.deltaTime * 5);
transform.rotation = Quaternion.Lerp(transform.rotation, latestRot, Time.deltaTime * 5);
r.velocity = velocity;
r.angularVelocity = angularVelocity;
}
}
void OnCollisionEnter(Collision contact)
{
if (!photonView.IsMine)
{
Transform collisionObjectRoot = contact.transform.root;
if (collisionObjectRoot.CompareTag("Player"))
{
//Transfer PhotonView of Rigidbody to our local player
photonView.TransferOwnership(PhotonNetwork.LocalPlayer);
}
}
}
}
- Attach PUN2_RigidbodySync to both Cubes and also assign it to Photon View "Observed Components":
We also need to make some changes to the PUN2_PlayerSync script from the Multiplayer tutorial:
- Open PUN2_PlayerSync.cs
- In void Start(), inside if(photonView.IsMine) add this code:
//Player is local
gameObject.tag = "Player";
//Add Rigidbody to make the player interact with rigidbody
Rigidbody r = gameObject.AddComponent<Rigidbody>();
r.isKinematic = true;
So now void Start() should look like this:
// Use this for initialization
void Start()
{
if (photonView.IsMine)
{
//Player is local
gameObject.tag = "Player";
//Add Rigidbody to make the player interact with rigidbody
Rigidbody r = gameObject.AddComponent<Rigidbody>();
r.isKinematic = true;
}
else
{
//Player is Remote, deactivate the scripts and object that should only be enabled for the local player
for (int i = 0; i < localScripts.Length; i++)
{
localScripts[i].enabled = false;
}
for (int i = 0; i < localObjects.Length; i++)
{
localObjects[i].SetActive(false);
}
}
}
By adding a Rigidbody component we make sure that the player instance can interact with other Rigidbodies and by changing the tag to "Player" we can detect whether it was a local instance that collided with a Rigidbody.
- Save the GameLevel Scene after everything is done.
Now let's make a build and test it!
Everything works as expected, now Rigidbodies can be synced over Network while still being interactable.