在光子统一中序列化 Vector3d 数据类型

Posted

技术标签:

【中文标题】在光子统一中序列化 Vector3d 数据类型【英文标题】:Serialize Vector3d datatype in photon unity 【发布时间】:2021-07-14 16:26:56 【问题描述】:

我想在 PUN RPC 参数中使用 vector3d 数据类型,但 Photon 不接受 vector3d。我们需要注册并序列化它以在 RPC 方法中使用。我的序列化代码有一些问题。

namespace VoxelPlay

    internal static class MyCustomDataType
    
        
        internal static void Register()
        

            PhotonPeer.RegisterType(typeof(Vector3d), (byte)'V', SerializeVector3d, DeserializeVector3d);

        

        public static readonly byte[] memVector3d = new byte[3 * 4];

        private static short SerializeVector3d(StreamBuffer outStream, object customobject)
        
            Vector3d vo = (Vector3d)customobject;

            int index = 0;
            lock (memVector3d)
            
                byte[] bytes = memVector3d;
                Protocol.Serialize(vo.x, bytes, ref index);      //Getting error: Cannot convert from double to short
                Protocol.Serialize(vo.y, bytes, ref index);
                Protocol.Serialize(vo.z, bytes, ref index);
                outStream.Write(bytes, 0, 3 * 4);
            
            return 3 * 4;
        

        private static object DeserializeVector3d(StreamBuffer inStream, short length)
        
            Vector3d vo = new Vector3d();
            lock (memVector3d)
            
                inStream.Read(memVector3d, 0, 3 * 4);
                int index = 0;
                Protocol.Deserialize(out vo.x, memVector3d, ref index);
                Protocol.Deserialize(out vo.y, memVector3d, ref index);
                Protocol.Deserialize(out vo.z, memVector3d, ref index);
            

            return vo;
        
    

【问题讨论】:

我建议您尝试将 vo.x, vo.y, vo.z 转换为 double 以消除注释错误。 好的,我试试这个 @JesúsNarváez 如果错误已经表明给定的double 不能转换为short(该方法期望的类型)然后将(已经!)double 值转换为@987654325 @ 不会有太大帮助 ;) 我通过提取 x、y 和 z 坐标将 vector3d 数据类型转换为 vector3 解决了这个问题。感谢您的帮助。 【参考方案1】:

ExitGames.Client.Photon.Protocol.Serialize 实际上只(反)序列化shortintfloat

但是,您可以轻松定义自己的 double (de) 序列化,例如使用BitConverter 和/或Buffer.BlockCopy

请注意,您的数组还是错误的!一个double has not 4 but rather 8 bytes

我建议你使用sizeof 来处理这类事情;)

internal static class MyCustomDataType

    internal static void Register()
    
        PhotonPeer.RegisterType(typeof(Vector3d), (byte)'V', SerializeVector3d, DeserializeVector3d);
    

    // Note: Your array had the wrong length anyway!
    // double is not 4 but 8 bytes!
    // Instead of magic numbers rather use sizeof in general
    public const short ByteSize = 3 * sizeof(double);
    public static readonly byte[] memVector3d = new byte[ByteSize];

    private static short SerializeVector3d(StreamBuffer outStream, object customobject)
    
        var vo = (Vector3d)customobject;

        var index = 0;
        lock (memVector3d)
        
            ToBytes(vo.x, memVector3d, ref index);      
            ToBytes(vo.y, memVector3d, ref index);
            ToBytes(vo.z, memVector3d, ref index);
            outStream.Write(memVector3d, 0, ByteSize);
        
        return ByteSize;
    

    private static object DeserializeVector3d(StreamBuffer inStream, short length)
    
        var vo = new Vector3d();
        lock (memVector3d)
        
            inStream.Read(memVector3d, 0, ByteSize);
            int index = 0;
            ReadDouble(out vo.x, memVector3d, ref index);
            ReadDouble(out vo.y, memVector3d, ref index);
            ReadDouble(out vo.z, memVector3d, ref index);
        

        return vo;
    

    private static void byte[] ToBytes(double input, byte[] bytes, ref offset)
    
        // Converts the double to a byte[] of length "sizeof(double)" (=8)
        var doubleBytes = BitConverter.ToBytes(input);
        // Copy the content of the doubleSize into the combined bytes
        // starting at current offset
        Buffer.BlockCopy(doubleBytes, 0, bytes, offset, doubleBytes.Length);
        // increase by the copied size
        offset += doubleBytes.Length;
    

    private static void ReadDouble(out double value, byte[] bytes, ref offset)
    
        // Reads "sizeof(double)" (=8) byes starting at given offset
        // and converts them to a double
        value = BitConverter.ToDouble(bytes, offset);
        // Increase by the amount of read bytes
        offset+=sizeof(double);
        return output;
    


或者一个可能稍微更有效的方法是直接使用Buffer.BlockCopy 来转换像

internal static class MyCustomDataType

    internal static void Register()
    
        PhotonPeer.RegisterType(typeof(Vector3d), (byte)'V', SerializeVector3d, DeserializeVector3d);
    

    // Note: Your array had the wrong length anyway!
    // double is not 4 but 8 bytes!
    // Instead of magic numbers rather use sizeof in general
    public const short ByteSize = 3 * sizeof(double);
    public static readonly byte[] memVector3d = new byte[ByteSize];
    public static readonly double[] doubles = new double[3];

    private static short SerializeVector3d(StreamBuffer outStream, object customobject)
    
        var vo = (Vector3d)customobject;

        lock (memVector3d)
        
            // Fill the doubles array
            doubles[0] = vo.x;
            doubles[1] = vo.y;
            doubles[2] = vo.z;

            // directly copy that array on the bytes level
            Buffer.BlockCopy(floats, 0, memVector3d, 0, ByteSize);
            
            outStream.Write(memVector3d, 0, ByteSize);
        
        return ByteSize;
    

    private static object DeserializeVector3d(StreamBuffer inStream, short length)
    
        var vo = new Vector3d();
        lock (memVector3d)
        
            inStream.Read(memVector3d, 0, ByteSize);

            // Just the other way round directly copy into the doubles array on byte level
            Buffer.BlockCopy(memVector3d, 0, floats, 0, ByteSize);
            
            vo.x = doubles[0];
            vo.y = doubles[1];
            vo.z = doubles[2];
        

        return vo;
    


现在请允许我问一个问题:你真的需要 Vector3ddouble 值吗?

Unity 中的所有内容基本上都使用float,所以在某些时候你无论如何都会失去精度。那么为什么在同步时不丢失精度呢?

并且 afaik Vector3 在 Photon 中具有内置支持,因此无需编写/维护自定义代码。


如果在同步时丢失精度是可以的,但你仍然想使用Vector3d,你可以简单地使用

Protocol.Serialize((float)vo.x, bytes, ref index);
Protocol.Serialize((float)vo.y, bytes, ref index);
Protocol.Serialize((float)vo.z, bytes, ref index);

然后接收只需确保它使用float 版本

Protocol.Deserialize(out float x, memVector3d, ref index);
Protocol.Deserialize(out float y, memVector3d, ref index);
Protocol.Deserialize(out float z, memVector3d, ref index);

vo.x = x;
vo.y = y;
vo.z = z;
        

但话又说回来,为什么不首先使用Vector3,假设它如前所述直接支持。

【讨论】:

感谢您的帮助。我正在开发一个体素游戏,我希望将 vector3d 的 voxelCenter 数据类型传递到 RPC 的参数中。

以上是关于在光子统一中序列化 Vector3d 数据类型的主要内容,如果未能解决你的问题,请参考以下文章

如何在统一光子中使用 Rpc(脚本不起作用)

如何统一使用光子网络连接到我们自己的专用服务器?(自托管)

C#Vector3D运算符' - '不能应用t类型'Vector3D'和double

光子中的 RPC 不通过网络发送数据

光子服务器注册自定义类型

Java 数组