Sincronizza i corpi rigidi sulla rete usando PUN 2

La sincronizzazione degli oggetti in PUN 2 è semplice, ma per quanto riguarda la sincronizzazione dei corpi rigidi?

A differenza dei normali GameObject, Rigidbody è influenzato anche dalla gravità (se non dalla cinematica) e anche da altri oggetti. Quindi, invece di sincronizzare solo la trasformazione dell'oggetto, dobbiamo anche sincronizzare un paio di parametri aggiuntivi, come velocity e angularVelocity.

In questo post, mostrerò come creare corpi rigidi interattivi che possono essere influenzati da ogni giocatore nella stanza e sincronizzati sulla rete.

Unity versione utilizzata in questo tutorial: Unity 2018.3.0f2 (64-bit)

Parte 1: Configurazione di PUN 2 e Multiplayer Esempio

Abbiamo già un tutorial su come impostare un esempio multiplayer usando PUN 2, controlla il link qui sotto:

Crea una partita multiplayer in Unity 3D usando PUN 2

Torna una volta che hai finito di impostare un progetto multiplayer in modo che possiamo continuare.

In alternativa, puoi risparmiare tempo scaricando il progetto sorgente da qui.

Parte 2: Aggiunta di corpi rigidi interattivi

Se hai seguito il tutorial sopra, ora avresti 2 scene "GameLobby" e "GameLevel"

  • Apri la scena "GameLevel" e crea un paio di cubi (GameObject -> Oggetto 3D -> Cubo)

  • Aggiungi un componente Rigidbody a ciascun cubo
  • Aggiungi un componente PhotonView a ogni cubo

Ora dobbiamo creare un nuovo script che sincronizzi i Rigidbodies sulla rete.

  • Crea un nuovo script e chiamalo 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);
            }
        }
    }
}
  • Collega PUN2_RigidbodySync a entrambi i cubi e assegnalo anche a Photon View "Observed Components":

Abbiamo anche bisogno di apportare alcune modifiche allo script PUN2_PlayerSync dal tutorial Multiplayer:

  • Apri PUN2_PlayerSync.cs
  • In void Start(), all'interno di if(photonView.IsMine) aggiungi questo codice:
            //Player is local
            gameObject.tag = "Player";
            //Add Rigidbody to make the player interact with rigidbody
            Rigidbody r = gameObject.AddComponent<Rigidbody>();
            r.isKinematic = true;

Quindi ora void Start() dovrebbe assomigliare a questo:

    // 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);
            }
        }
    }

Aggiungendo un componente Rigidbody ci assicuriamo che l'istanza del player possa interagire con altri Rigidbodies e modificando il tag in "Player" possiamo rilevare se lo era un'istanza locale che si è scontrata con un Rigidbody.

  • Salva la scena del livello di gioco dopo che tutto è stato fatto.

Ora creiamo una build e testiamola!

Sharp Coder Lettore video

Tutto funziona come previsto, ora Rigidbodies può essere sincronizzato sulla rete pur rimanendo interagibile.