Sharp Coder is reader-supported, meaning when you buy through links on our site, we may earn an affiliate commission.
HomeBlogUnityNetworking

Sync Rigidbodies Over Network Using PUN 2

0 Comments ยท May 31, 2019
11069

Syncing Objects in PUN 2 is quite simple (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 as well. So instead of syncing just the object Transform, we also need to sync a couple of additional parameters, such as velocity and angularVelocity.

In this post, I will be showing 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 couple of Cubes (GameObject -> 3D Object -> Cube)

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

Now we need to create a new Script which 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":

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 < localObjects.Length; i++)
            {
                localObjects[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!

Sharp Coder Video Player

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

You Do Not Have To Do Everything Yourself
Search from thousands of ready-to-use Unity assets.
Visit Asset Store