[Unity Mirror] 序列化

81 篇文章 31 订阅

英文原文:

https://mirror-networking.gitbook.io/docs/guides/serialization

  本页深入介绍了序列化,有关基础知识,请参阅 DataTypes

  Mirror 使用 Wea​​ver 为类型创建序列化和反序列化函数。 Weaver 在Unity使用 Mono.Cecil 编译 dll 后编辑 dll。这允许Mirror具有许多复杂的功能,例如 SyncVar、ClientRpc 和消息序列化,而无需用户手动设置所有内容。


规则和提示

  Weaver 可以做的事情有一些规则和限制。一些功能增加了复杂性并且难以维护,因此尚未实现。这些功能并非不可能实现,如果对它们有很高的需求,可以添加。

  • 您应该能够为任何类型编写自定义读/写函数,并且 Weaver 将使用。
    • 这意味着如果存在不支持的类型,例如 int[][],创建自定义读/写函数将允许您在 SyncVar/ClientRpc/etc 中同步 int[][]
  • 如果您有一个类型的字段不该被序列化,您可以使用 [System.NonSerialized] 标记该字段,weaver 将忽略它
不支持的类型

如上所述,由于它们会增加复杂性,其中一些类型不受支持。

NOTE: 此列表中的类型可以具有自定义writers。

  • 锯齿状和多维数组
  • 从 UnityEngine.Component 继承的类型
  • UnityEngine.Object
  • UnityEngine.ScriptableObject
  • 泛型类型,例如 MyData
  • 自定义读/写必须声明 T,例如 MyData
  • 接口
  • 引用自身的类型
内置读写函数

  Mirror 提供了一些内置的读/写功能。它们可以在 NetworkReaderExtensions 和 NetworkWriterExtensions 中找到。

  这是具有内置功能的类型的非竞争列表,请查看上面的类以查看完整列表。

  • 大多数 C# 原始类型
  • 常见的 Unity 结构
  • Vector3
  • Quaternion
  • Rect
  • Ray
  • Guid
  • NetworkIdentity, GameObject, Transform
NetworkIdentity, GameObject, Transform

  Object的netId通过网络发送,另一端返回具有相同netId的Object。如果 netId 为零或未找到对象,则将返回 null。

生成的读写函数

Weaver 将生成读写函数

  • Classes or Structs
  • Enums
  • Arrays
    • 例如 int[]
  • ArraySegments
    • 例如 ArraySegment
  • Lists
    • 例如 List

类和结构

  Weaver 将读取/写入类型中的每个公共字段,除非该字段被标记为 [System.NonSerialized]。如果类或结构中有不受支持的类型,Weaver 将无法为其创建读/写函数。

注意:Weaver 不检查属性

枚举

Weaver 将使用枚举的底层类型来读取和写入它们。默认情况下,这是 int。

例如 Switch 将使用字节读/写函数进行序列化

public enum Switch : byte
{
    Left,
    Middle,
    Right,
}

集合

Weaver 将为上面列出的集合生成写入。 Weaver 将使用元素的读/写功能。该元素必须具有读/写功能,因此必须是受支持的类型,或者具有自定义读/写功能。

例如:

  • float[] 是受支持的类型,因为 Mirror 具有内置的浮点读/写函数。
  • MyData[] 是受支持的类型,因为 Weaver 能够为 MyData 生成读/写函数
public struct MyData
{
    public int someValue;
    public float anotherValue;
}

添加自定义读写函数

读写函数是静态方法,形式如下:

public static void WriteMyType(this NetworkWriter writer, MyType value)
{
    // write MyType data here
}

public static MyType ReadMyType(this NetworkReader reader)
{
    // read MyType data here
}

  最佳实践是制作读/写函数扩展方法,以便可以像 writer.WriteMyType(value) 一样调用它们。

  将它们称为 ReadMyType 和 WriteMyType 是一个好主意,这样它们的类型就很明显了。不过函数名无所谓,weaver不管叫什么都应该能找到。

属性示例

Weaver 不会编写属性,但可以使用自定义编写器通过网络发送它们。

如果您想为您的属性设置私有集,这可能很有用

public struct MyData
{
    public int someValue { get; private set; }
    public float anotherValue { get; private set; }

    public MyData(int someValue, float anotherValue)
    {
        this.someValue = someValue;
        this.anotherValue = anotherValue;
    }
}

public static class CustomReadWriteFunctions 
{
    public static void WriteMyType(this NetworkWriter writer, MyData value)
    {
        writer.WriteInt32(value.someValue);
        writer.WriteSingle(value.anotherValue);
    }

    public static MyData ReadMyType(this NetworkReader reader)
    {
        return new MyData(reader.ReadInt32(), reader.ReadSingle());
    }
}

不支持的类型示例

  Rigidbody 是不受支持的类型,因为它继承自 Component。但是可以添加一个自定义writer,以便在附加的情况下使用 NetworkIdentity 进行同步。

public struct MyCollision
{
    public Vector3 force;
    public Rigidbody rigidbody;
}

public static class CustomReadWriteFunctions
{
    public static void WriteMyCollision(this NetworkWriter writer, MyCollision value)
    {
        writer.WriteVector3(value.force);

        NetworkIdentity networkIdentity = value.rigidbody.GetComponent<NetworkIdentity>();
        writer.WriteNetworkIdentity(networkIdentity);
    }

    public static MyCollision ReadMyCollision(this NetworkReader reader)
    {
        Vector3 force = reader.ReadVector3();

        NetworkIdentity networkIdentity = reader.ReadNetworkIdentity<NetworkIdentity>();
        Rigidbody rigidBody = networkIdentity != null
            ? networkIdentity.GetComponent()
            : null;

        return new MyCollision
        {
            force = force,
            rigidbody = rigidBody,
        };
    }
}

  以上是 MyCollision 的函数,但您可以为 Rigidbody 添加函数,让 weaver 为 MyCollision 生成一个编写器。

public static class CustomReadWriteFunctions
{
    public static void WriteRigidbody(this NetworkWriter writer, Rigidbody rigidbody)
    {
        NetworkIdentity networkIdentity = rigidbody.GetComponent<NetworkIdentity>();
        writer.WriteNetworkIdentity(networkIdentity);
    }

    public static Rigidbody ReadRigidbody(this NetworkReader reader)
    {
        NetworkIdentity networkIdentity = reader.ReadNetworkIdentity();
        Rigidbody rigidBody = networkIdentity != null
            ? networkIdentity.GetComponent<Rigidbody>()
            : null;

        return rigidBody;
    }
}

调试

  Weaver 修改后,您可以使用 ILSpy 和 dnSpy 等工具查看已编译的代码。这有助于理解和调试 Mirror 和 Weaver 的功能。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity中的protobuf序列化是一种将数据结构转化为字节流的方法,以便在网络传输、存储或在不同平台之间传递数据。它基于Google的protobuf (Protocol Buffers)协议,能够高效地序列化和反序列化复杂的数据结构。 在Unity中使用protobuf序列化需要进行以下几个步骤: 1. 定义消息结构:首先需要在.proto文件中定义要序列化的数据结构。这包括定义消息的字段、枚举、嵌套消息等。可以指定每个字段的类型(整数、浮点数、字符串等)和标签(用于标识字段的唯一性)等信息。 2. 编译.proto文件:使用protobuf编译器将.proto文件编译为相应语言的代码。在Unity中可以使用Protobuf-net等第三方插件来生成C#代码。 3. 序列化数据:在需要序列化数据的地方,将数据按照定义好的消息结构进行赋值,并使用protobuf提供的方法将其序列化为字节流。 4. 反序列化数据:在接收端或需要解析数据的地方,使用protobuf提供的方法将字节流反序列化为消息对象,然后可以通过读取字段的方式获取其中的数据。 使用unity protobuf序列化的好处是: 1. 空间效率高:protobuf采用二进制格式进行序列化,可以将数据压缩为较小的字节流,减少网络传输和存储的空间成本。 2. 速度快:protobuf的序列化和反序列化速度较快,可以更有效地处理大量的数据。 3. 跨平台兼容性好:使用protobuf序列化后的数据可以在不同平台、不同语言之间共享和传输,无需担心兼容性问题。 总之,Unity中的protobuf序列化是一种在网络传输和数据存储中高效、方便的数据序列化方法,可以帮助开发者更好地处理数据结构和跨平台数据传输的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值