Synchronisieren Sie Rigidbodies über das Netzwerk mit PUN 2

Das Synchronisieren von Objekten in PUN 2 ist einfach, aber wie sieht es mit der Synchronisierung von Rigidbodies aus?

Im Gegensatz zu normalen GameObjects wird Rigidbody auch von der Schwerkraft (wenn nicht kinematisch) und anderen Objekten beeinflusst. Anstatt also nur die Transformation des Objekts zu synchronisieren, müssen wir auch einige zusätzliche Parameter synchronisieren, wie zum Beispiel velocity und angularVelocity.

In diesem Beitrag werde ich zeigen, wie man interaktive Rigidbodies erstellt, die von jedem Spieler im Raum beeinflusst und über das Netzwerk synchronisiert werden können.

Unity In diesem Tutorial verwendete Version: Unity 2018.3.0f2 (64-Bit)

Teil 1: Einrichten von PUN 2 und Multiplayer-Beispiel

Wir haben bereits ein Tutorial zum Einrichten eines Multiplayer-Beispiels mit PUN 2, schauen Sie sich den Link unten an:

Erstellen Sie ein Multiplayer-Spiel in Unity 3D mit PUN 2

Kommen Sie zurück, sobald Sie mit der Einrichtung eines Multiplayer-Projekts fertig sind, damit wir fortfahren können.

Alternativ können Sie Zeit sparen, indem Sie das Quellprojekt von hier herunterladen.

Teil 2: Hinzufügen interaktiver starrer Körper

Wenn Sie dem obigen Tutorial gefolgt wären, hätten Sie jetzt 2 Szenen "GameLobby" und "GameLevel"

  • Öffnen Sie die "GameLevel"-Szene und erstellen Sie ein paar Würfel (GameObject -> 3D-Objekt -> Würfel).

  • Fügen Sie jedem Würfel eine Rigidbody-Komponente hinzu
  • Fügen Sie jedem Cube eine PhotonView-Komponente hinzu

Jetzt müssen wir ein neues Skript erstellen, das die Rigidbodies über das Netzwerk synchronisiert.

  • Erstellen Sie ein neues Skript und nennen Sie es 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);
            }
        }
    }
}
  • Hängen Sie PUN2_RigidbodySync an beide Cubes an und weisen Sie es auch Photon View "Observed Components" zu:

Wir müssen auch einige Änderungen am PUN2_PlayerSync-Skript aus dem Multiplayer-Tutorial vornehmen:

  • Öffnen Sie PUN2_PlayerSync.cs
  • Fügen Sie in void Start() innerhalb von if(photonView.IsMine) diesen Code hinzu:
            //Player is local
            gameObject.tag = "Player";
            //Add Rigidbody to make the player interact with rigidbody
            Rigidbody r = gameObject.AddComponent<Rigidbody>();
            r.isKinematic = true;

Nun sollte void Start() also so aussehen:

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

Durch das Hinzufügen einer Rigidbody-Komponente stellen wir sicher, dass die Player-Instanz mit anderen Rigidbodies interagieren kann und indem wir das Tag in "Player" ändern, können wir erkennen, ob es eine lokale Instanz war, die mit einem Rigidbody kollidierte.

  • Speichern Sie die GameLevel-Szene, nachdem alles erledigt ist.

Jetzt erstellen wir einen Build und testen ihn!

Sharp Coder Videoplayer

Alles funktioniert wie erwartet, jetzt können Rigidbodies über das Netzwerk synchronisiert werden und sind dennoch interaktiv.