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

2023-10-25 17:48

本文主要是介绍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参数详解)

这篇关于Unity问题与解决方案:Photon同步数据的四种方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/284190

相关文章

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

java实现docker镜像上传到harbor仓库的方式

《java实现docker镜像上传到harbor仓库的方式》:本文主要介绍java实现docker镜像上传到harbor仓库的方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 前 言2. 编写工具类2.1 引入依赖包2.2 使用当前服务器的docker环境推送镜像2.2

Redis出现中文乱码的问题及解决

《Redis出现中文乱码的问题及解决》:本文主要介绍Redis出现中文乱码的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 问题的产生2China编程. 问题的解决redihttp://www.chinasem.cns数据进制问题的解决中文乱码问题解决总结

MyBatisPlus如何优化千万级数据的CRUD

《MyBatisPlus如何优化千万级数据的CRUD》最近负责的一个项目,数据库表量级破千万,每次执行CRUD都像走钢丝,稍有不慎就引起数据库报警,本文就结合这个项目的实战经验,聊聊MyBatisPl... 目录背景一、MyBATis Plus 简介二、千万级数据的挑战三、优化 CRUD 的关键策略1. 查

python实现对数据公钥加密与私钥解密

《python实现对数据公钥加密与私钥解密》这篇文章主要为大家详细介绍了如何使用python实现对数据公钥加密与私钥解密,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录公钥私钥的生成使用公钥加密使用私钥解密公钥私钥的生成这一部分,使用python生成公钥与私钥,然后保存在两个文

mysql中的数据目录用法及说明

《mysql中的数据目录用法及说明》:本文主要介绍mysql中的数据目录用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、版本3、数据目录4、总结1、背景安装mysql之后,在安装目录下会有一个data目录,我们创建的数据库、创建的表、插入的

全面解析MySQL索引长度限制问题与解决方案

《全面解析MySQL索引长度限制问题与解决方案》MySQL对索引长度设限是为了保持高效的数据检索性能,这个限制不是MySQL的缺陷,而是数据库设计中的权衡结果,下面我们就来看看如何解决这一问题吧... 目录引言:为什么会有索引键长度问题?一、问题根源深度解析mysql索引长度限制原理实际场景示例二、五大解决

Springboot如何正确使用AOP问题

《Springboot如何正确使用AOP问题》:本文主要介绍Springboot如何正确使用AOP问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录​一、AOP概念二、切点表达式​execution表达式案例三、AOP通知四、springboot中使用AOP导出

springboot项目打jar制作成镜像并指定配置文件位置方式

《springboot项目打jar制作成镜像并指定配置文件位置方式》:本文主要介绍springboot项目打jar制作成镜像并指定配置文件位置方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录一、上传jar到服务器二、编写dockerfile三、新建对应配置文件所存放的数据卷目录四、将配置文

Python中Tensorflow无法调用GPU问题的解决方法

《Python中Tensorflow无法调用GPU问题的解决方法》文章详解如何解决TensorFlow在Windows无法识别GPU的问题,需降级至2.10版本,安装匹配CUDA11.2和cuDNN... 当用以下代码查看GPU数量时,gpuspython返回的是一个空列表,说明tensorflow没有找到