Unity问题与解决方案:Photon同步数据的四种方式

从我目前get到的点钟,大概是有三个方法,第一个方法是使用PhotonNetwork.RPC方法,该方法接收方法名,发送对象(PhotonPlayer或PhotonTarget)和方法参数,并调用声明[PUNRPC]的相关方法,但根据源码:

//<summary>
/// Internal to send an RPC on given PhotonView. Do not call this directly but use: PhotonView.RPC!
/// </summary>
internal static void RPC(PhotonView view, string methodName, PhotonTargets target, bool encrypt, params object[] parameters)

很明显该方法并不被推荐适用(Do not call this directly but use: PhotonView.RPC!

第二个方法则是按照PhotonView同步的方式,模仿PhotonTransformView等写一个自己的脚本,如果要仿照的话,PhotonRigidbodyView的实现较为简单,可以参考,来看一下原码:

using System;
using UnityEngine;
using Object = System.Object;

/// <summary>
/// This class helps you to synchronize the velocities of a physics RigidBody.
/// Note that only the velocities are synchronized and because Unitys physics
/// engine is not deterministic (ie. the results aren't always the same on all
/// computers) - the actual positions of the objects may go out of sync. If you
/// want to have the position of this object the same on all clients, you should
/// also add a PhotonTransformView to synchronize the position.
/// Simply add the component to your GameObject and make sure that
/// the PhotonRigidbodyView is added to the list of observed components
/// </summary>
[RequireComponent(typeof(PhotonView))]
[RequireComponent(typeof(Rigidbody))]
[AddComponentMenu("Photon Networking/Photon Rigidbody View")]
public class PhotonRigidbodyView : MonoBehaviour, IPunObservable
{
    [SerializeField]
    bool m_SynchronizeVelocity = true;

    [SerializeField]
    bool m_SynchronizeAngularVelocity = true;

    Rigidbody m_Body;

    void Awake()
    {
        this.m_Body = GetComponent<Rigidbody>();
    }

    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {


        if (stream.isWriting == true)
        {
            if (this.m_SynchronizeVelocity == true)
            {
                stream.SendNext(this.m_Body.velocity);
            }

            if (this.m_SynchronizeAngularVelocity == true)
            {
                stream.SendNext(this.m_Body.angularVelocity);

            }
        }
        else
        {
            if (this.m_SynchronizeVelocity == true)
            {
                this.m_Body.velocity = (Vector3)stream.ReceiveNext();
            }

            if (this.m_SynchronizeAngularVelocity == true)
            {
                this.m_Body.angularVelocity = (Vector3)stream.ReceiveNext();
            }
        }
    }
}

从中不难看到,实现的关键是实现IPunObservable接口和接口对应的方法,该实现通过判断stream的读写状态来执行SendNextReceiveNext,要注意发送和接收的数据流和接收逻辑必须是一一对应的,否则会出现值无法对应的情况。

另外一个特别需要注意的问题!如果是基础数据类型,是可以直接SendNext和ReceiveNext的,但是如果是自定义类型,需要在Photon中进行注册,相关文档在PhotonUnity的Document中有,可以去看一下链接,这里列出序列化的代码实例:

如对于我自定义的一个增益属性类Plugin

public class Camp
{
    public int camp;
}

有两种注册方式(其实差别不大,只是其中一个使用了Photon提供给我们的流)来进行注册,这里只展示第一种

即扩展该类,添加序列化和反序列化的静态方法:

public class Camp
{
  public int camp;

  public static byte[] SerializableClass(object plugin)//参数必须是object
    {
        Campp = (Camp) plugin;

        int intlen = Marshal.SizeOf(typeof(int));
        /*
         * 创建字节序列
         */
        byte[] bytes = new byte[intlen];

        /*
         * 序列化
         */
        int index = 0;
        Protocol.Serialize(p.camp, bytes, ref index);

        return bytes;
    }

    public static object DeserializeClass(byte[] bytes)
    {
        Plugin plugin = new Plugin();
        int index = 0;
        Protocol.Deserialize(out plugin.camp, bytes, ref index);

        return plugin;
    }
}

随后在任意位置注册一次即可:

PhotonPeer.RegisterType(typeof(Plugin), (byte) 'a',SerializableClass,DeserializeClass);

注意关于第二个是类似占位符的形式,已经注册过的byte不能再次注册,以及被官方注册过的字节有:W/V/Q/P这四种,分别代表Vector3,Vector2,QuaternionPhotonPlayer

然后关于一些其他的字节之间相互转化的方法可以参考一下极客学院的关于Photon的一个公开课,链接,这里就不详细展开了


然后我基于build模式和最近rx系列的思路写了一个传输类,能简化一下stream传输的逻辑,也附在这吧


public class PhotonTranser
{
    private PhotonStream stream;

    public static PhotonTranser create(PhotonStream stream)
    {
        return new PhotonTranser(stream);
    }

    private PhotonTranser(PhotonStream stream)
    {
        this.stream = stream;
    }

    public PhotonTranser sendnext(object value,bool realSend)
    {
        if (realSend &&stream.isWriting) 
        {
            stream.SendNext(value);
        }

        return this;
    }
    public PhotonTranser sendnext(object value)
    {
        if (stream.isWriting)
        {
            stream.SendNext(value); 
        }
        return this;
    }

    public PhotonTranser receive(Func<object, object> value,bool realRecv)
    {
        if (realRecv &&!stream.isWriting)
        {
            value(stream.ReceiveNext());
        }
        return this;
    }
    public PhotonTranser receive(Func<object, object> value)
    {
        if (!stream.isWriting)
        {
            value(stream.ReceiveNext());    
        }

        return this;
    }

    public void finish(){}

}

按照该类的实现,PhotonRigidbodyView中的OnPhotonSerializeView实现就可以改为:

PhotonTranser.create(stream)
            .sendnext(this.m_Body.velocity, this.m_SynchronizeVelocity)
            .sendnext(this.m_Body.angularVelocity,this.m_SynchronizeAngularVelocity)
            .receive(new Func<object, object>(delegate(object o)
            {
                this.m_Body.velocity = (Vector3) o;
                return null;

            }), this.m_SynchronizeVelocity)
            .receive(new Func<object, object>(delegate(object o)
            {
                this.m_Body.angularVelocity = (Vector3) o;
                return null;

            }),this.m_SynchronizeAngularVelocity  )
            .finish();

感觉这样下来会就比较直观,不过由于C#半路出家..一些语法不是很了解,不清楚有没有更简单的实现方式,比如lambda等(猜的),有人有思路可以提供一下

第三种方法有条件,就是只能在用PhotonNetwork.Instantiate实例化对象的时候传递,具体的方式可以参考一下我的这一篇博客:

Photon实例化(PhotonNetwork.Instantiate参数详解)

猜你喜欢

转载自blog.csdn.net/sailist/article/details/81274466