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;
    }
}

相关推荐

  1. Unity-序列序列

    2024-04-29 16:10:02       62 阅读
  2. 序列序列

    2024-04-29 16:10:02       47 阅读
  3. 序列序列xml bin

    2024-04-29 16:10:02       53 阅读
  4. Springboot Jackson 序列序列配置

    2024-04-29 16:10:02       47 阅读
  5. 在php中的序列序列

    2024-04-29 16:10:02       35 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-29 16:10:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-29 16:10:02       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-29 16:10:02       82 阅读
  4. Python语言-面向对象

    2024-04-29 16:10:02       91 阅读

热门阅读

  1. vue 动态改变css样式

    2024-04-29 16:10:02       29 阅读
  2. 1013: 【C1】【循环】【for】求整数的和与均值

    2024-04-29 16:10:02       28 阅读
  3. 【类和对象】

    2024-04-29 16:10:02       35 阅读
  4. 「PHP系列」PHP Error(错误处理)讲解

    2024-04-29 16:10:02       34 阅读
  5. 42 v-model

    2024-04-29 16:10:02       35 阅读
  6. Hillstone 各系列设备Console波特率说明

    2024-04-29 16:10:02       31 阅读
  7. CentOS7 命令行下连接WiFi

    2024-04-29 16:10:02       31 阅读
  8. 【HTML】实现 pre 标签内容超出自动换行

    2024-04-29 16:10:02       32 阅读