Синхронизация твердых тел по сети с помощью PUN 2

Синхронизировать объекты в PUN 2 просто, но как насчет синхронизации Rigidbodies?

В отличие от обычных игровых объектов, на Rigidbody также влияет гравитация (если не кинематика) и другие объекты. Поэтому вместо синхронизации только Transform объекта нам также необходимо синхронизировать пару дополнительных параметров, таких как velocity и angularVelocity.

В этом посте я покажу, как создавать интерактивные твердые тела, на которые может влиять каждый игрок в комнате и которые синхронизируются по сети.

Unity версия, используемая в этом руководстве: Unity 2018.3.0f2 (64-разрядная версия)

Часть 1. Настройка PUN 2 и пример многопользовательской игры

У нас уже есть руководство о том, как настроить пример многопользовательской игры с использованием PUN 2, перейдите по ссылке ниже:

Создайте многопользовательскую игру в Unity 3D, используя PUN 2.

Вернитесь, как только закончите настройку многопользовательского проекта, чтобы мы могли продолжить.

Альтернативно, вы можете сэкономить время, скачав исходный проект по адресу здесь.

Часть 2. Добавление интерактивных твердых тел

Если вы следовали инструкциям выше, у вас теперь будет 2 сцены "GameLobby" и "GameLevel"

  • Откройте сцену "GameLevel" и создайте пару кубов (GameObject -> 3D Object -> Cube).

  • Добавьте компонент Rigidbody в каждый куб.
  • Добавьте компонент PhotonView в каждый куб.

Теперь нам нужно создать новый скрипт, который будет синхронизировать Rigidbodies по сети.

  • Создайте новый скрипт и назовите его 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);
            }
        }
    }
}
  • Прикрепите PUN2_RigidbodySync к обоим кубам, а также назначьте его Photon View "Observed Components":

Нам также необходимо внести некоторые изменения в скрипт PUN2_PlayerSync из руководства по многопользовательской игре:

  • Откройте PUN2_PlayerSync.cs.
  • В 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;

Итак, теперь void Start() должен выглядеть так:

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

Добавляя компонент Rigidbody, мы гарантируем, что экземпляр игрока может взаимодействовать с другими Rigidbody, а изменив тег на "Player", мы можем определить, был ли локальный экземпляр столкнувшимся с Rigidbody.

  • Сохраните сцену GameLevel после того, как все будет сделано.

Теперь давайте сделаем сборку и протестируем ее!

Sharp Coder Видео проигрыватель

Все работает как положено, теперь Rigidbodies можно синхронизировать по сети, сохраняя при этом возможность взаимодействия.

Рекомендуемые статьи
Создайте многопользовательскую игру в Unity, используя PUN 2.
Создайте многопользовательскую автомобильную игру с помощью PUN 2
PUN 2 Компенсация задержки
Unity добавляет многопользовательский чат в комнаты PUN 2
Photon Network (Classic) Руководство для начинающих
Создание многопользовательских сетевых игр в Unity
Учебное пособие по онлайн-таблице лидеров Unity