Sync Rigidbodies Over Network Using PUN 2

Sync Rigidbodies Over Network Using PUN 2

by NSDG • May 31, 2019 • 0 Comments
253
Syncing Objects in PUN 2 is quite easy (as demonstrated in this tutorial). But what about syncing Rigidbodies?

Unlike regular GameObjects, Rigidbody is also affected by the Gravity (if not Kinematic) and other Objects aswell (in case of collision). So instead of syncing just the Object Transform, we also have to sync couple of additional parameters, such as velocity and angularVelocity.

In this post I will be showing how to make an 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 setup a multiplayer example using PUN 2, check the link below:

Make a multiplayer game in Unity 3D using PUN 2

Come back once you 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 a tutorial above you would now have 2 Scenes "GameLobby" and "GameLevel"

  • Open the "GameLevel" Scene and create couple of Cubes (GameObject -> 3D Object -> Cube)



  • Add a Rigidbody component to each Cube
  • Add PhotonView component to each Cube

Now we have to create a new Script which will sync the Rigidbodies over 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":



And lastly we need to make some changes to 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 < localObject.Length; i++)
            {
                localObject[i].SetActive(false);
            }
        }
    }

By adding a Rigidbody component we make sure that 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.