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!

Sharp Coder Video Player

Everything works as expected, now Rigidbodies can be synced over Network while still being interactable.

Suggested Articles
Make a Multiplayer Game in Unity using PUN 2
Make a Multiplayer Car Game with PUN 2
Photon Network (Classic) Beginner's Guide
PUN 2 Lag Compensation
Unity Adding Multiplayer Chat to the PUN 2 Rooms
Building Multiplayer Networked Games in Unity
Multiplayer Data Compression and Bit Manipulation