废话就不说了。
这里记录三种使用pbn的方式:
1、手动方式
2、动态方式
3、proto文件方式
代码示例是编译不过的,主要是学习交流之用。
使用方式如代码:
using UnityEngine; using UnityEditor; using System.Collections.Generic; using ProtoBuf; using System; using System.IO; using System.Xml.Serialization; namespace StaticUsageDemo { [ProtoContract] [Serializable] public class TestDataTable { [ProtoMember(1)] public TestData[] Data = new TestData[0]; } [ProtoContract] [Serializable] public class TestData { [ProtoMember(6, IsRequired = true)] public float value { get; set; } [ProtoMember(1, IsRequired = true)] public int UserID { get; set; } [ProtoMember(2, IsRequired = false)] public string UserName { get; set; } [ProtoMember(3, IsRequired = false)] public string UserName2 { get; set; } [ProtoMember(4, IsRequired = false)] public string UserName3 { get; set; } [ProtoMember(5, IsRequired = false)] public string UserName4 { get; set; } } public static class StaticUsageDemo { [MenuItem("Metadata/Create")] public static void CreateFile() { UGE.Utility.HPTimer.Init(); var dataTable = new TestDataTable(); int count = 100000; using(UGE.Utility.TimeTracker.Record("Create Data")) { List<TestData> list = new List<TestData>(count); for ( int i =0; i < count; ++i ) { list.Add(new TestData { UserID = 0, UserName = "AAA", UserName2 = "AAA", UserName3 = "AAA", UserName4 = "AAA", value = 1, }); } dataTable.Data = list.ToArray(); } //TestDataTable dataXML2; //{ // Stream fileXML = File.Create("Tools/Output/TestData.xml"); // try // { // XmlSerializer xmler = new XmlSerializer(dataTable.GetType()); // xmler.Serialize(fileXML, dataTable); // // read xml version data; // fileXML.Seek(0, SeekOrigin.Begin); // dataXML2 = xmler.Deserialize(fileXML) as TestDataTable; // Debug.Log("dataXML:" + dataXML2.Data[0].UserName); // } // finally // { // fileXML.Close(); // } //} // use data; { Stream file = File.Open("Tools/Output/TestData.dat", FileMode.OpenOrCreate); try { Debug.Log("tableCount:" + dataTable.Data.Length); using ( UGE.Utility.TimeTracker.Record("Serialize Data") ) { Serializer.Serialize<TestDataTable>(file, dataTable); } file.Seek(0, SeekOrigin.Begin); TestDataTable table; using ( UGE.Utility.TimeTracker.Record("Deserialize Data") ) { table = Serializer.Deserialize<TestDataTable>(file); } Debug.Log("tableCount:" + table.Data.Length); Debug.Log(string.Format("method 2:{0}", table.Data[1].UserName)); } finally { file.Close(); } } Debug.Log(UGE.Utility.TimeTracker.Dump()); return; } } }
Type type = mt.Type; FieldInfo[] fields = type.GetFields((BindingFlags)( BindingFlags.Instance | BindingFlags.Public )); foreach ( var field in fields ) { mt.Add(mt.GetNextFieldNumber(), field.Name); }
message TestData { required int32 ID=1; required string UserName=2; required string UserName2=3; required string UserName3=4; required string UserName4=5; required string UserName5=6; required float value=7; } message TestDataTable { repeated TestData data=1; }
protogen -i:test.proto -o:test.cs -ns:UGE.Metadata -p:import=UGE
这句话的意思是, 输入test.proto文件, 给我生成 test.cs 文件, 代码在 namespace UGE.Metadata里, 顺便引用下 using UGE.
例子如下:
//------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ // Generated from: test.proto namespace test { [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"TestData")] public partial class TestData : global::ProtoBuf.IExtensible { public TestData() {} private int _ID; [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"ID", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] public int ID { get { return _ID; } set { _ID = value; } } private string _UserName; [global::ProtoBuf.ProtoMember(2, IsRequired = true, Name=@"UserName", DataFormat = global::ProtoBuf.DataFormat.Default)] public string UserName { get { return _UserName; } set { _UserName = value; } } private string _UserName2; [global::ProtoBuf.ProtoMember(3, IsRequired = true, Name=@"UserName2", DataFormat = global::ProtoBuf.DataFormat.Default)] public string UserName2 { get { return _UserName2; } set { _UserName2 = value; } } private string _UserName3; [global::ProtoBuf.ProtoMember(4, IsRequired = true, Name=@"UserName3", DataFormat = global::ProtoBuf.DataFormat.Default)] public string UserName3 { get { return _UserName3; } set { _UserName3 = value; } } private string _UserName4; [global::ProtoBuf.ProtoMember(5, IsRequired = true, Name=@"UserName4", DataFormat = global::ProtoBuf.DataFormat.Default)] public string UserName4 { get { return _UserName4; } set { _UserName4 = value; } } private string _UserName5; [global::ProtoBuf.ProtoMember(6, IsRequired = true, Name=@"UserName5", DataFormat = global::ProtoBuf.DataFormat.Default)] public string UserName5 { get { return _UserName5; } set { _UserName5 = value; } } private float _value; [global::ProtoBuf.ProtoMember(7, IsRequired = true, Name=@"value", DataFormat = global::ProtoBuf.DataFormat.FixedSize)] public float value { get { return _value; } set { _value = value; } } private global::ProtoBuf.IExtension extensionObject; global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); } } [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"TestDataTable")] public partial class TestDataTable : global::ProtoBuf.IExtensible { public TestDataTable() {} private readonly global::System.Collections.Generic.List<TestData> _data = new global::System.Collections.Generic.List<TestData>(); [global::ProtoBuf.ProtoMember(1, Name=@"data", DataFormat = global::ProtoBuf.DataFormat.Default)] public global::System.Collections.Generic.List<TestData> data { get { return _data; } } private global::ProtoBuf.IExtension extensionObject; global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); } } }
没有测试在这种情况下怎么继承。估计这是正确使用pb的方式,应该有很多文档说明。
我在这里记录的很简单,主要是指明了关键的工作流。
在实际使用的时候,需要根据情况自行安排使用方式,所以再往多写就啰嗦了。
顺带一提我选择的是第二种方式。
各方式速度比较:
100,000 十万条数据, 5个string 1个int32 , 1个float 。数据没有多样化。
ms Write Read
方式1 430+ 2000+
方式2 1000+ 1300+
方式3 我没测过,应该和1类似
和json之类的压缩率比较没意义,pb使用varint,本来就很擅长压缩。
算是大致有个印象吧