Unity 二进制读写小框架

文章目录

前言

在Unity开发过程中,与后台交互时经常需要处理大型数据文件。当遇到一个近2MB的本地JSON文件需要解析为对象时,使用传统的JSON解析方案(无论是Unity自带的JsonUtility还是流行的Newtonsoft Json.NET)都面临着显著的性能瓶颈。在实测中,这些方案解析时间可能长达3秒以上,即使经过优化也需要约1.5秒,同时还伴随着大量的GC(垃圾回收)开销。

针对这一问题,我们开发了一个高性能的二进制序列化框架------SJBinary。经过测试,该框架在性能表现上超越了市面上多数JSON框架和Unity自带的BinaryFormatter,接近Protobuf-net的性能水平,虽然略低于MessagePack,但其集成简单、代码量小的特点使其成为Unity项目中数据序列化的优秀选择。

实测数据显示,使用SJBinary创建10万个对象并序列化为二进制数据仅需480多毫秒,反序列化过程仅需340多毫秒,性能提升显著。
提示:二进制数据处理本质上比JSON处理更加高效,因为它避免了文本解析的复杂过程,直接使用二进制格式表示数据。

框架获取与集成

sjgenerate下载链接

1.点击上面链接即可下载

2.下载完后将SjBinary拖入项目即可

3.定义类并添加注解[SJBinary]及实现接口ISJSerializable

使用方法

基本配置

csharp 复制代码
[SJBinary]
public class A : ISJSerializable
{
    public string name;
    public int age;

    public void Deserialize(SJBinaryReader reader)
    {
     throw new System.NotImplementedException();
     }

    public void Serialize(SJBinaryWriter writer)
    {
     throw new System.NotImplementedException();
    }
}

自动生成序列化方法

  • 点击Unity顶部菜单栏:Tools -> SJBinary -> Generate Methods
  • 系统将自动实现Deserialize和Serialize方法

生成的类结构如下:

csharp 复制代码
[SJBinary]
public class B : ISJSerializable
{
    public string name;
    public int age;

    public void Serialize(SJBinaryWriter writer)
    {
        writer.Write(this.name);
        writer.Write(this.age);
    }

    public void Deserialize(SJBinaryReader reader)
    {
        this.name = reader.ReadString();
        this.age = reader.ReadInt32();
    }
}

实战示例

以下是一个完整的使用示例,展示了如何在Unity中使用SJBinary进行高效序列化和反序列化:

csharp 复制代码
public class ErJinZhiSample : MonoBehaviour
{//优化前497 343ms
    private List<byte[]> serializedData = new List<byte[]>();
    public List<TestA> deserializedObjects = new List<TestA>();
    private const int count = 100000;
    public Button btnLoad;
    public Button btnCreate;

    // 复用的 reader
    private SJBinaryReader reader;

    void Start()
    {
        btnLoad.onClick.AddListener(TestLoad);
        btnCreate.onClick.AddListener(TestCreate);
    }
    void TestCreate()
    {
        serializedData.Clear();
        var writer = new SJBinary.SJBinaryWriter(1024); // 可复用缓冲区
        Stopwatch sw = Stopwatch.StartNew();

        for (int i = 0; i < count; i++)
        {
            writer.Reset(); // 复用缓冲区
            TestA obj = new TestA
            {
                age = i,
                name = $"Name_{i}",
                time = DateTime.Now.Ticks,
                actions = "Run,Jump",
                actionsList = new List<string> { "Run", "Jump", "Walk" },
                ids = new List<int> { 1, 2, 3 },
                pads = new List<int> { 10, 20, 30 }
            };

            obj.Serialize(writer);

            // 保存二进制数据(注意这里保存的是 ArraySegment 以避免复制)
            serializedData.Add(writer.GetBytes());
        }

        sw.Stop();
        UnityEngine.Debug.Log($"[Create] Created and serialized {count} objects in {sw.ElapsedMilliseconds} ms");
    }

    void TestLoad()
    {
        deserializedObjects.Clear();
        Stopwatch sw = Stopwatch.StartNew();

        for (int i = 0; i < count; i++)
        {
            byte[] data = serializedData[i];

            // 复用 reader
            if (reader == null)
                reader = new SJBinary.SJBinaryReader(data);
            else
                reader.SetBuffer(data, 0, data.Length);

            TestA obj = new TestA();
            obj.Deserialize(reader);
            deserializedObjects.Add(obj);
        }

        sw.Stop();
        UnityEngine.Debug.Log($"[Load] Deserialized {count} objects in {sw.ElapsedMilliseconds} ms");
    }
}

技术原理与优势

二进制序列化的优势

1.高效性能: 二进制格式避免了文本解析的复杂性,直接读写内存数据
2.紧凑体积: 二进制数据通常比等效的JSON文本小30%-50%
3.低GC开销: 通过缓冲区复用和零拷贝技术大幅减少垃圾回收压力

SJBinary的设计特点

1.简单集成: 只需添加注解和接口实现,无需复杂配置
2.代码生成: 提供工具自动生成序列化代码,减少手动编写错误
3.缓冲区复用: 支持读写缓冲区的复用,极大减少内存分配
4.类型安全: 编译时检查类型一致性,避免运行时错误

最佳实践建议

1.适当初始化缓冲区大小: 根据典型数据大小初始化缓冲区,避免频繁扩容
2.复用读写器实例: 特别是在循环中处理多个对象时
3.处理版本兼容性: 当数据结构变化时,需要考虑向后兼容性策略
4.异常处理: 在序列化和反序列化过程中添加适当的异常处理机制

适用场景

SJBinary特别适用于以下场景:

  • 需要频繁序列化/反序列大量数据的游戏
  • 对性能敏感的网络通信应用
  • 需要持久化大量游戏状态的单机游戏
  • 需要快速加载大型配置文件的应用程序

总结

SJBinary为Unity开发者提供了一个简单易用且高性能的二进制序列化解决方案。通过其简洁的API设计和高效的实现,它能够显著提升数据处理的性能,同时保持较低的内存开销。对于需要处理大量数据的Unity项目,SJBinary是一个值得考虑的轻量级解决方案。

如果您在使用过程中有任何建议或问题,欢迎在评论区留言反馈。我们将持续优化和改进这个框架,以满足更多开发场景的需求。