文章目录
前言
在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处理更加高效,因为它避免了文本解析的复杂过程,直接使用二进制格式表示数据。
框架获取与集成
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是一个值得考虑的轻量级解决方案。
如果您在使用过程中有任何建议或问题,欢迎在评论区留言反馈。我们将持续优化和改进这个框架,以满足更多开发场景的需求。