欢迎光临散文网 会员登陆 & 注册

Godot with C#

2023-02-01 08:03 作者:紧果呗  | 我要投稿

Godot 有两套语言支持构建:

  • - 默认的支持 GDScript,GDExtension

  • - 另一套则集成了 .NET 6 支持 C#, GDScript, GDExtension。

要使用 C# 语言进行 Godot 项目开发,系统上需要安装 .NET SDK 6.0 或 7.0,.NET 7.0 的支持还不完善。相比 GDScript 脚本,C# 是一个编译型的高级语言,Godot 通过开源的 Mono 6.x 框架支持 C# 8.0 语言版本。与作用快速原型开发使用 GDScript 脚本不同,每次执行之前都需要进行编译,以生成最新的 C# 程序集。但是,作为预编译语言,它的运行效率虽然不能和 C++ 看齐,但比 GDSCript 有非常大的效率提升,简单情况有 4x 提升。并且,在使用的便利程序上,要比 C++ 好,所以在不是极限性能需要情况下,C# 是值得一试的方案。


怎么选开发语言,就是权衡开发效率与程序运行性能:

  • - 选择 GDScript 可以快速地做原型迭代;

  • - 选择 C# 一方面提升了性能,另外它比 C++/Rust 更容易上手,同时编译速度也不太慢;

  • - 最后,极致性能要求,那么就选择 C++/Rust 折腾去吧!

Godot 3.2.3 开始,不需安装 Mono SDK,除非需要从源代码构建 Godot,但是 .NET SDK 还是要安装。注意,使用的 Godot 要与 SDK 的版本比特位一致,建议使用 64-bit 的版本。由于 Godot 只提供了 C# 的最小支持,可以考虑使用外部编辑器,如 Visual Studio Code,以提供更完善的自动完成、调试等功能。Godot 目前支持以下作用外部编辑器,可以通过 Editor → Editor Settings → Mono → Editor 修改:

  • - Visual Studio 2019

  • - Visual Studio Code

  • - MonoDevelop

  • - Visual Studio for Mac

  • - JetBrains Rider

以 VSCode 配置为例,最新的 Godot 4 不需要配置 Builds:

  • - Set **Mono** -> **Editor** -> **External Editor** to **Visual Studio Code**.

  • - Set **Mono** -> **Builds** -> **Build Tool** to **dotnet CLI**.

并且在 Visual Studio Code 中安装以下扩展:

  • - [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp)

  • - [Mono Debug](https://marketplace.visualstudio.com/items?itemName=ms-vscode.mono-debug)

  • - [C# Tools for Godot](https://marketplace.visualstudio.com/items?itemName=neikeq.godot-csharp-vscode)

C# Tools for Godot 扩展支持 Godot 3.2.2 以上版本,可以提供以下功能:

  • - Debugging.

  • - Launch a game directly in the Godot editor from VSCode.

  • - Additional code completion for Node paths, Input actions, Resource paths, Scene paths and Signal names.

如果使用 Linux 操作系统,需要安装 Mono SDK 以支持 Godot 的 C# 工具插件。

配置 VSCdoe 以实现 Godot 工程的运行:

  • - 执行菜单 Run → **Add Configuration...**  → **C# Godot**

  • - 编辑配置文件 ``tasks.json`` 和 ``launch.json``,使用 executable 和 Command 指向正确的 Godot 可执行文件。

完成以上配置后,就可以直接在 VScode 中运行 Godot 项目:

  • - 使用命令调板,Ctrl-Shift-P 打开面板并输入 C# Godot 查询相关命令;

  • - 在状态栏中,点击 Godot Project 字样处选择工程目录;

  • - 在状态栏中,点击 Play in Editor (mono) 字样处运 Godot 工程;

  • - 直接打开 Run and Debug 侧栏面板选择 ``launch.json`` 配置的调试的方式:

  •     - Play in Editor 直接通过编辑器中运行;

  •     - Launch 执行 `Godot.exe --path ${workspaceRoot}`

  •     - Launch (Select Scene) 选择场景执行 `Godot.exe --path ${workspaceRoot} ${command:SelectLaunchScene}`

  •     - Attach 附着到本地调试进程;

如果工程不能正常运行,而 VScode 又看不到错误信息,就可以到 Godot 的 MSBuild 面板查询编译日志信息。

配置文件 ``tasks.json`` 默认只提供了一个构建任务,可以通过菜单 Terminal → Run Build Task... 执行: 

如果添加运行配置时,没有提供 **C# Godot** 配置,请检查以上三个扩展是否完成安装,并且处于启用状态。配置好开发环境,就可以在 Godot 给节点附加脚本时,在 Attach Script 对话框中选择 C# 语言。

Node 节点类扩展代码示例:

使用 C# 与 GDScript 的一些差异:

  • - C# 使用 ``PascalCase`` 代码风格,GDSCript/C++ 使用 ``snake_case`` 风格,`AddChild()` vs. `add_child()`。

  • - C# 类名要求代码文件名一致,否由会提示 *"Cannot find class XXX for script res://XXX.cs"*

  • - C# 中使用 Godot 命令空间下的 GD 管理 @GDscript 和 @GlobbalScope 全局函数符号。

  • - C# 导出符号生效之前,需要重新编译程序集,通过 Godot 界面右上角的 Build 按钮构建工程。

  • - C# 语句使用分号作为结束符号,而 GDScript 不需要。

  • - C# 中以 `Godot.Object` 作为所有类型的基类,新版本更名为 `Godot.GodotObject`。

  • - C# 使用 this 引用当前类实例,GDScript 使用 self 引用当前类实例。

在 C# 中,也不能像 GDScript 那样,直接拖动节点到脚本中创建引用,也不能使用 onready,而需要在 Ready 这类事件中,使用 FindNode 或者 GetNode 获取节点引用:

使用 C# 进行 Godot 项目开发,还需要解决以下这些基本问题:

  • - 不同语言之间的相互调用问题;

  • - Godot 信号系统的使用方式的差异;

  • - C# 与 GDScript API 之间的差异;

  • - Godot 不同版本之间的差异处理等等;

Godot 考虑到了不同语言之间相互调用,C# 调用 GDScript API 或者属性读写使用 GodotObject 提供的方法:

而在 GDscript 访问 C# API 则是直接调用,就像使用其它 GDScript 对象一样,实例化操作如下所示:


注意,实例化得到的类型以 Godot 内置类型为准,而不是按 C# 或 GDScript 中声明的类型作为判断标准。

比如,后面的 MyNode2D 在使用 GDScript `is` 关键字做类型判断时,需要使用内置类型 Node 作为参考。

C# 调用 GDScript API 时注意,如果第一个参数是一个数组,那么就需要显式转换为 `object` 类型。否则,

数组元素就会被当作一个参数使用,并可能导致函数签名不匹配。

编写 C# 类代码时注意,类名与 ``.cs`` 代码文件名一致,否则提示错误:

    Invalid call. Nonexistent function `new` in base.

比如,MyCoolNode.cs 文件就应该定义 MyCoolNode 类型。并且需要继承自 ``Godot.Object`` 或其它子类。最后,C# 工程文件 ``.csproj``中要正确引用``.cs`` 文件,这样才会生效。

Godot 4.x Mono 信号机制在 C# 使用委托机制实现,并且可以使用更高效的 += 和 -= 运算符监听、或者取消监听。另外,Connect 方法也有更新,使用 Callable 对象包装回调函数及回调参数。另外,通过节点的嵌套类 SignalName 可以访问信号名称,它继承自 GodotObject.SignalName。清理节点时,Godot 会负责所有信号监听事件的清理:

以下是 Callable 类参考文档中展示的用法,使用了嵌套类 MethodName 或者 nameof 获取方法名称。但是在默认参数绑定操作上有差别,GDScript 中可以直接调用 Callable 对象的 bind() 方法绑定默认参数。而目前在 C# 中给信号绑定默认参数则需要使用 lambdas 来构造出一个包含默认参数的 Callable 对象。如果方法没有参数,也没有返回值,可以包装成 Action,由 Callable.From() 再包装成可调用对象。

Godot 4.x Mono 所有信号定义名称使用 EventHandler 结尾,定义好信号后,完成后,Godot 在幕后自动使用 C# `event` 关键字创建相应的事件。然后,可以像其他 Godot 信号一样使用自定义信号事件。

注意,类型定义使用的 partial class,即表示类定义代码是分开文件存放的,还可以包含在其它代码文件。

除了直接使用委托方式定义信号,还可以使用 AddUserSignal() 方法添加自定义信号。

Godot 3.x Mono 自定义信号的使用有些差别,获取信号名称使用 nameof(MySignal) 语法,并且信号连接依然是使用节点的 Connect 方法进行。信号名称的获取这种操作一致性不够好,例如,获取按钮节点的信号就不能使用 nameof(Button.Pressed) 这样的表达,而是直接使用 "pressed" 字符串字面量。

总结起来,C# 中有三种获取或使用信号名称的方式,Godot 3.x 只支持前两种:

  • - `EmitSignal("MySignal");` 直接使用字符串字面量

  • - `EmitSignal(nameof(MySignal));` 使用 nameof 关键字

  • - `EmitSignal(SignalName.MySignal);` 使用内嵌类 SignalName

使用自定义信号可能遇到的问题是:调用 EmitSignal() 时报错,表示信号不存在,而调用 AddUserSignal() 手动添加信号时,又表示不能重复添加已经定义的信号,这可能是定义信号的代码没有写到类体内部。

一些 GDScript 遇不到的问题,如下:

一个类型中结构体,Vector2,在 C# 中是通过拷贝进行赋值的,也就是说在获取 Position 属性时,获取到的是一个副本,对这个副本赋值并不会影响到原来归属的节点。解决方法:创建一个变量引用 Position 再进行赋值操作。在 C# 10 版本中,可以对结构体使用 with 表达式来解决这种问题。

通常,使用 C# 编程而不是 GDScript,一个主要的目的可能是为了提升程序运行速度,所以应该避免编写无效率的代码,以下是两种节点的移动方式对比,后者更高效:

给 Godot C# API 传递原始的 byte[] 或者字符串,需要 marshalling 操作,这相对不够高效。隐式转换 string 为 NodePath 或 StringName 会产生原生互操作和 marshalling 成本,因为字符串必须编组并传递给相应的原生构造函数。


参考:

  • - http://www.mono-project.com/docs/about-mono/compatibility

  • - https://docs.godotengine.org/en/3.5/tutorials/scripting/c_sharp/c_sharp_features.html

  • - https://docs.godotengine.org/en/3.5/tutorials/scripting/c_sharp/c_sharp_differences.html

  • - https://docs.godotengine.org/en/stable/tutorials/scripting/cross_language_scripting.html

  • - https://docs.godotengine.org/en/stable/tutorials/scripting/creating_script_templates.html

  • - https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/index.html

  • - https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/c_sharp_basics.html

  • - https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/c_sharp_exports.html

  • - https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/c_sharp_differences.html

  • - https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/c_sharp_signals.html

  • - https://docs.godotengine.org/en/latest/contributing/development/core_and_modules/object_class.html#doc-binding-properties-using-set-get-property-list

  • - [Bunnymark](https://github.com/cart/godot3-bunnymark)

  • - [Godot.NET.Sdk](https://www.nuget.org/packages/Godot.NET.Sdk/#versions-body-tab)

  • - [Mono SDK](https://www.mono-project.com/download/stable/#download-lin)

  • - [.NET SDK](https://dotnet.microsoft.com/download)

  • - [Visual Studio Code](https://code.visualstudio.com/)

  • - [Godot 3.5.1 Mono](https://downloads.tuxfamily.org/godotengine/3.5.1/mono/)

  • - [CustomType for Godot 3.x](https://github.com/Jeangowhy/Godot-Tour/tree/main/mono-3x/addons/CustomType)

  • - [CustomType for Godot 4.x](https://github.com/Jeangowhy/Godot-Tour/tree/4.x/mono-4x/addons/CustomType)


Godot with C#的评论 (共 条)

分享到微博请遵守国家法律