MeteorCat / Godot 集成 Protobuf

Created Sun, 28 Sep 2025 20:34:52 +0800 Modified Wed, 29 Oct 2025 23:24:59 +0800

Godot 集成 Protobuf

Godot 版本默认指 Godot C# 版本

目前的 Godot 默认版本的 GDScript 脚本第三方的 Protobuf 库水平高低不平, 并且本身 Godot ABI 维护变动之下很容易某些属性|特性|方法在更新之后出现异常

所以如果需要采用 Godot 做网络序列化传输, 建议采用 C# 版本而非官方默认版, 从而才能利用上 .net 相关周边的第三方组件优势.

以上文档都是采用 LTS 版本的 .net 库相关

默认构建之后的项目之下有 XXXX.csprojC# 配置文件, 这里就是主要的 .net 相关项目配置:

<!-- 请留意这里, 这里代表默认采用 Godot-NET 自定义绑定, 也就是其实内部并不是完全支持全部 net 相关 -->
<!-- 所以有些打包命令可能无法唤起, 像是 Protobuf 可能在常规 C# 项目当中引入可能没办法触发 Protobuf-Tools 自动打包命令  -->
<!-- 需要注意的是因为采用的是 Godot.Net 绑定, 所以这里面默认加载目录分隔符号是 '/' 而不用区分 '\\' 和 '/' -->
<Project Sdk="Godot.NET.Sdk/4.5.0">

    <!-- 项目配置的全局属性 -->
    <PropertyGroup>
        <!-- 以下是系统默认生成, 其实就是 net9 支持安卓打包和默认采用 .net8LTS -->
        <TargetFramework>net8.0</TargetFramework>
        <TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net9.0</TargetFramework>
        <EnableDynamicLoading>true</EnableDynamicLoading>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>disable</Nullable>

        <!-- 这里就是我自定义的全局属性 -->
        <ProtobufInputDir>Proto</ProtobufInputDir>
        <ProtobufOutputDir>Generated/Protobuf</ProtobufOutputDir>

        <!-- 这里就是通过官方文档获取的环境变量, 注意: C#配置通过 $(XXX) 可以加载到全局很多变量, 包括外部的环境变量  -->
        <!-- 这里就是加载外部环境变量配置的三个值, 从外部加载可以避免硬编码后续不好调整 -->
        <!-- Manual:https://chromium.googlesource.com/external/github.com/grpc/grpc/+/HEAD/src/csharp/BUILD-INTEGRATION.md -->
        <ProtobufExecBIN>$(PROTOBUF_PROTOC)</ProtobufExecBIN>
        <ProtobufExecOS>$(PROTOBUF_TOOLS_OS)</ProtobufExecOS>
        <ProtobufExecCPU>$(PROTOBUF_TOOLS_CPU)</ProtobufExecCPU>
    </PropertyGroup>

    <!-- 引入的第三方库, 利用 C# 可以获取到 .net 环境更加稳定且丰富的第三方生态 -->
    <!-- 通过 dotnet add package Google.Protobuf 或者 Install-Package Google.Protobuf 安装 Protobuf 核心 -->
    <!-- 通过 dotnet add package Google.Protobuf.Tools 或者 Install-Package Google.Protobuf.Tools 安装扩展支持, 这个扩展工具不一定有用 -->
    <ItemGroup>
        <PackageReference Include="Google.Protobuf" Version="3.32.1"/>
        <PackageReference Include="Google.Protobuf.Tools" Version="3.32.1" PrivateAssets="All"/>
    </ItemGroup>

    <!-- 加载Protobuf打包命令, 这里面的 'res://' 是 Godot.Net 引入的特性, 也就是当前的游戏资源起始目录  -->
    <!-- 这里的命令可能没办法被 Google.Protobuf.Tools 加载到, 所以可以酌情删除处理, 主要打包命令还是要手动编写  -->
    <ItemGroup>
        <Protobuf Include="res://$(ProtobufInputDir)/**/*.proto"
                  ProtoRoot="res://$(ProtobufInputDir)"
                  OutputDir="res://$(ProtobufOutputDir)"
                  CompileOutputs="true"
                  GrpcServices="None">
        </Protobuf>
    </ItemGroup>

    <!-- 引入目前项目的 Proto 相关 proto 文件, 展示在工程项目之中 -->
    <ItemGroup>
        <Content Include="Proto/**/*.proto"/>
    </ItemGroup>


    <!-- 编译时候触发的打印内容 -->
    <Target Name="PrintVariables" BeforeTargets="CoreCompile">
        <Message Importance="high" Text="Protobuf Execute Binary -> $(ProtobufExecBIN)"/>
        <Message Importance="high" Text="Protobuf Execute OS -> $(ProtobufExecOS)"/>
        <Message Importance="high" Text="Protobuf Execute CPU -> $(ProtobufExecCPU)"/>
        <Message Importance="high" Text="Protobuf Input Files - > res://$(ProtobufInputDir)/**/*.proto"/>
        <Message Importance="high" Text="Protobuf Output Directory -> res://$(ProtobufOutputDir)"/>
    </Target>


    <!-- 编译的时候触发的 Protobuf 打包命令 -->
    <Target Name="ProtobufGenerate" BeforeTargets="CoreCompile">

        <!-- 确认 Protobuf 输出目录是否存在, 如果不存在就是创建目录 -->
        <MakeDir Directories="$(ProjectDir)$(ProtobufOutputDir)"
                 Condition="!Exists('$(ProjectDir)$(ProtobufOutputDir)')"/>

        <!-- 读取 Protobuf 之中的所有 *.proto 文件, 准备后续进行打包生成 xxxx.cs 原生文件  -->
        <ItemGroup>
            <ProtobufFiles Include="$(ProtobufInputDir)/**/*.proto"/>
        </ItemGroup>

        <!-- 注意: 以下才是关键, 官方 Protobuf 库起始说直接会加载 *.proto 文件, 但前提是 .net 环境而现在采用的是 Godot.Net, 所以不会自动输出生成 cs 文件 -->
        <!-- 因此这里需要手动去编写打包命令, 方便直接生成对应 Protobuf 类文件 -->

        <!-- 最终打包命令 -->
        <Exec Command="$(ProtobufExecBIN) -I=&quot;$(ProjectDir)$(ProtobufInputDir)&quot; --csharp_out=&quot;$(ProjectDir)$(ProtobufOutputDir)&quot; @(ProtobufFiles->'&quot;%(FullPath)&quot;', ' ')"/>

    </Target>
</Project>

这样配置之后就能正常输出需要 Protobuf C# 文件, 但是需要时刻记住这里用的是 Godot.Net 而非官方原生的 Net 版本, 所以很多问题都要自己去处理解决.

另外环境变量修改最好重启和注销重新进入当前管理员账号让系统变量生效

这里补充定义常见的 Protobuf 格式:

syntax = "proto3";

// C# 打包的命令空间, 其实就是 [你项目空间].Generated.Protobuf 就行了
// 更加干脆一点就是直接复制你的 xxx.csproj 文件删除掉 .csproj 即可
option csharp_namespace = "DotNetProject.Generated.Protobuf";

// 网络数据流请求: 客户端→服务端
// 这里的id是为了映射对应的标识的二级protobuf二进制数据
message BytesRequest{
  int32 id = 1;
  bytes message = 2;
}

// 网络数据流响应: 服务端→客户端
message BytesResponse{
  int32 id = 1;
  bytes message = 2;
}

// 一些常规的运行时异常, 有时候需要把错误返回给客户端的命令行输出
message PanicRuntimeException{
  int32 status = 1;
  string message = 2;
}

注意: 在打出正式包的时候, 应该把 *.proto 文件全部排除, 不要在正式环境引用

这部分可以查看简单测试样例: protobuf-cluster