作为GDScript开发者的C#体验!其一

2025-04-09

本文将记录我使用C#的过程中踩下的各种坑。鄙人用过几年gdscript, 对这门灵活的语言深表赞赏。但出于工程需求,我也有了想要尝试C#的想法,故有了此文。倘若我折腾的经历能帮到你,那也是很好的。

下载引擎和Dotnet!

官网上提供了Dotnet版本的godot下载。只需下载即可。简单。

image-ibrD.png

对了,还请顺带确保你安装了C#相关套件——我是说,一个成熟的IDE(比如Visual Studio, VSCode, 或者Rider)。正好赶上Rider允许非商业免费使用,考虑到这个游戏是个测试工程,我就打算用rider来写了。据说rider用起来还挺傻瓜的?

记得到官网上寻找Dotnet 8.x的SDK!目前最新版本的戈多已经要求使用至少8.x的dotnet了。不知道是好事还是坏事?

开始表演:开启C#项目?

随便创建一个项目,找一个节点,给它赋予C#脚本,戈多就会为你的项目生成“解决方案”?

image-RekV.png

我不知道所谓“解决方案”是什么东西。在一番询问AI后,得到了以下结果:

1. .csproj 文件(C# 项目文件)

  • 是什么
    一个 XML 格式的文件,定义了一个 C# 项目的结构和依赖项(类似 Java 中的 pom.xmlbuild.gradle)。
    Godot 会为你的游戏生成一个 .csproj 文件,例如 YourGame.csproj

  • 作用

    • 指定项目使用的 .NET 版本(如 .NET 6/7/8。最新版戈多要求8.x起步。)
    • 列出需要编译的 C# 文件(<Compile Include="Scripts/Player.cs" />
    • 声明 NuGet 包依赖(如 <PackageReference Include="ImGui.NET" />
    • 配置 Godot 特有的设置(如 <GodotProjectRoot>..</GodotProjectRoot>
  • Godot 中的示例

    <Project Sdk="Godot.NET.Sdk/4.1.3">
      <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
      </PropertyGroup>
      <ItemGroup>
        <Compile Include="Scripts/Player.cs" />
      </ItemGroup>
    </Project>
    

2. .sln 文件(解决方案文件)

  • 是什么
    一个文本文件,用于组织多个 .csproj 项目(类似 Java IDE 中的“Workspace”)。
    Godot 生成的解决方案默认只包含你的游戏项目(例如 YourGame.sln)。

  • 作用

    • 允许在 IDE(如 Visual Studio、Rider)中同时打开多个相关项目
    • 管理项目间的引用关系(比如依赖一个共享库)
    • 提供构建配置的全局设置
  • Godot 生成的示例

    Microsoft Visual Studio Solution File, Format Version 12.00
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YourGame", "YourGame.csproj", "{...}"
    EndProject
    

来安装外部包吧。但立刻遇到麻烦了?!

使用GDS期间,我非常喜欢ImGui这个插件。它似乎也有Dotnet版本的支持。二话不说,立刻安排上。

image-JSbP.png

随便创建一个小场景,随便赋予个初始默认C#脚本,点击“运行”,引擎会自动构建工程。。。

比起纯GDScript, dotnet版本要等上好几秒才能等到游戏显示窗口。嗯,为了安全性和可维护性,这点取舍是值得的。

嗯.. 不对。。这是?

啥??

image-QafZ.png

GDS这边则是只有用到的脚本才会参与游戏,即使你写了个语法完全狗屁不通的脚本,只要你只是把它放在某个目录里不动它也不调用/加载/引用它,它就不会给你的游戏带来错误。

看起来C#比GDS要严格多了呢。似乎 所有C#文件都会被纳入编译范围。这什么意思呢?这意味着,你在项目里拉的每一行C#💩,最终都逃不了编译器的法眼。

很显然我们刚刚安装的插件出了问题,导致编译器报错了?

啊。回过头来再看看我们的插件说明?

https://github.com/pkdawson/imgui-godot?tab=readme-ov-file#getting-started-c

啊,原来如此。我们需要用Nuget来安装包: ImGui.NET

等会,Nuget又是什么东西??

那Nuget是什么东西?Asset Lib?

其实,NuGet 就像 Godot 的 AssetLib,但专门管 C# 的代码库(比如 ImGui.NET)。当你写 using SomeLibrary; 时,NuGet 就是背后帮你下载这个库的工具。

让我们安装 ImGui.NET吧。 首先在项目的根目录下打开终端,输入:

dotnet add package ImGui.NET

如果你此时注意csproj文件,你会注意多了这么一行:

<PackageReference Include="ImGui.NET" Version="x.x.x" />

这就意味着,Dotnet在你的游戏项目里,成功安装了名为ImGui.NET的包。后续使用dotnet构建游戏时,会将这个包也一并纳入你的游戏,你可以在你的代码的任何地方使用它!

相较于戈多的Asset Lib, dotnet的这套管理策略似乎,更“保守”?或者说,更“安全”?一旦开发者数量多起来,这种死板的方式就成了保证项目不出问题的保障?谁知道呢?

"UnSafe Code"? 这又是啥?

好了,我们现在安装了ImGui.Net了。接下来总算可以Build了吧?

image-sBDZ.png

等等。。

“Allow Unsafe Blocks”...

说到了这里,咱们就不得不了解下Unsafe是什么了。

字面意思:代码可以绕过 .NET 的安全检查,直接操作指针和内存地址(类似 C/C++)。

安全代码(默认) 不安全代码(unsafe
内存完全由 .NET 运行时管理 可以手动搞指针、内存地址
无法直接调用原生库 能和 C/C++ 库无缝互操作
性能稍低(有安全检查开销) 性能更高(但可能炸💥)

ImGui.NET底层需要和原生 C++ 版的 Dear ImGui 交互,必须通过指针传递数据,所以这种“不太安全”的代码,其实是必要的。倒不如说,如果想调用原生库,就必须使用不安全代码。

那具体怎么做?

其实,只要编辑项目的 .csproj 文件,在 <PropertyGroup> 中添加:

<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

即可。

编译通过,搞定!

image-YqzV.png

(嘛,虽说还是有些warning的,又装了俩插件,就当不存在吧。)