Unity | 集成 Protobuf(proto 转 cs 插件及序列化与反序列化)

1. 添加 dll

1. 下载 protobuf 源码

根据需要下载 protobuf 指定版本的源码,这里以 v3.21.12(protobuf-csharp-3.21.12.zip)为例:

下载地址:https://github.com/protocolbuffers/protobuf/releases」

2. 下载 Visual Studio 2022

曾尝试使用 Visual Studio 2021 版本编译,但是会报错,更新版本后,即可编译成功

下载地址:https://visualstudio.microsoft.com/zh-hans/downloads/」

3. 编译 dll

1. 使用 Visual Studio 2022 打开 protobuf 项目中的 Google.Protobuf.sln
2. 编译模式选择:Release
3. Google.Protobuf 右键选择生成 Google.Protobuf
4. 拷贝 dll

编译生成的 dll 在 csharp/src/Google.Protobuf/bin/Release 目录下

将 net45 目录下所有的 dll 文件拷贝到 Unity 指定目录中

2. proto 转 cs

1. 下载编译器

在 protobuf 源码链接中下载对应的平台的编译器(mac: protoc-21.12-osx-x86_64.zip,windows:protoc-21.12-win64.zip)

2. 命令行转换

在终端中进入到编译器所在的目录:bin/protoc,然后执行对应的命令

  • 转换指定 proto 文件:

    ./protoc ./Addressbook.proto --csharp_out=./

  • 转换指定目录下所有 proto 文件:

    ./protoc ./*.proto --csharp_out=./

将生成的 cs 文件,放入 Unity 项目中

3. 插件转换

除了直接使用命令行外,也可以在 Unity 中编写插件,利用 Process 执行命令行,转换 proto 文件:

复制代码
public class ProtoToClass
{
    [MenuItem("RavenKit/Proto To Class")]
    public static void GenerateClassFromProto()
    {
        string rootPath = Environment.CurrentDirectory;
        string protoPath = Path.Combine(rootPath, "Proto/");

        string protoc = Path.Combine(protoPath,
            RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "protoc.exe" : "protoc");

        string outPath = EditorPrefs.GetString(Application.identifier + "-ProtoClassPath");
        if (string.IsNullOrEmpty(outPath))
        {
            outPath = EditorUtility.OpenFolderPanel("选择存储目录", Application.dataPath, "");
            if (!string.IsNullOrEmpty(outPath))
            {
                EditorPrefs.SetString(Application.identifier + "-ProtoClassPath", outPath);
            }
        }

        string[] protoFiles = Directory.GetFiles(protoPath, "*.proto");
        foreach (var variable in protoFiles)
        {
            string argument = $"--csharp_out={outPath} --proto_path={protoPath} {variable}";
            Run(protoc, argument);
        }

        AssetDatabase.Refresh();
    }

    public static Process Run(string exe, string arguments)
    {
        ProcessStartInfo info = new ProcessStartInfo
        {
            FileName = exe,
            Arguments = arguments,
            CreateNoWindow = true,
            UseShellExecute = false,
            RedirectStandardOutput = true,
            RedirectStandardError = true,
        };

        Process process = Process.Start(info);
        process.WaitForExit();
        if (process.ExitCode != 0)
        {
            Debug.LogError($"Failed to Run {arguments}. Exit code: " + process.ExitCode);
        }

        return process;
    }
}

3. 序列化及反序列化

使用方法在官方的源码中也有详细的示例:

复制代码
byte[] bytes;
// Create a new person
Person person = new Person
{
    Id = 1,
    Name = "Foo",
    Email = "foo@bar",
    Phones = { new Person.Types.PhoneNumber { Number = "555-1212" } }
};
using (MemoryStream stream = new MemoryStream())
{
    // Save the person to a stream
    person.WriteTo(stream);
    bytes = stream.ToArray();
}
Person copy = Person.Parser.ParseFrom(bytes);

AddressBook book = new AddressBook
{
    People = { copy }
};
bytes = book.ToByteArray();
// And read the address book back again
AddressBook restored = AddressBook.Parser.ParseFrom(bytes);

// The message performs a deep-comparison on equality:
if (restored.People.Count != 1 || !person.Equals(restored.People[0]))
{
    throw new Exception("There is a bad person in here!");
}

反序列化时,除了使用 ParseFrom 方法

复制代码
AddressBook.Parser.ParseFrom(bytes) 

也可以使用 MergeFrom 方法

复制代码
message.MergeFrom(data);

将序列化和反序列化封装为通用的函数:

复制代码
public static class ProtobufUtility
{
    /// <summary>
    /// 序列化
    /// </summary>
    /// <param name="msg"></param>
    /// <returns></returns>
    public static byte[] Serialize(IMessage message)
    {
        using (MemoryStream stream = new MemoryStream())
        {
            message.WriteTo(stream);
            byte[] result = ms.ToArray();
            return result;
        }
    }

    /// <summary>
    /// 反序列化
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="data"></param>
    /// <returns></returns>
    public static T Deserialize<T>(byte[] data) where T : IMessage, new()
    {
        T message = new T();
        // message = (T)message.Descriptor.Parser.ParseFrom(data);
        message.MergeFrom(data);
        return msg;
    }
}
相关推荐
天人合一peng7 小时前
unity 生成标记根据背景色标记变色
unity·游戏引擎
天人合一peng11 小时前
unity 生成标记根据背景色变色为明显的颜色
unity·游戏引擎
魔士于安11 小时前
Unity 超市总动员 超市收银台 超市货架 超市购物手推车 超市常见商品
游戏·unity·游戏引擎·贴图·模型
CandyU211 小时前
Unity —— 数据持久化
unity·游戏引擎
zh路西法12 小时前
【Unity实现Oneshot胶卷显形】游戏窗口化与Win32API的使用
游戏·unity·游戏引擎
迪捷软件13 小时前
显控系统虚拟仿真的工程化路径
游戏引擎·cocos2d
凡情16 小时前
android隐私合规检测
android·unity
小贺儿开发16 小时前
Unity3D 本地 Stable Diffusion 文生图效果演示
人工智能·unity·stable diffusion·文生图·ai绘画·本地化
Swift社区17 小时前
传统游戏引擎 vs 鸿蒙 System 架构
架构·游戏引擎·harmonyos
mxwin1 天前
Unity Shader 半透明物体为什么不能写入深度缓冲?
unity·游戏引擎·shader