[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)
C# 中没有 class_name 这样的关键字用来注册自定义节点,这可以使用插件的 Custom types。通过菜单创建插件:Project → Project Settings → Plugins → Create new Plugin。使用 C# 创建插件时,注意脚本名称和类名要一致。
也可以使用 AssetLib → Plugins 打开插件管理面板,并且使用 AssetLib 提供的 Import 按钮来安装插件。开发好的插件只需要打包成 zip 文件,注意要将 addons 目录层级一并打包,Import 执行安装是直接解压文件到工程根目录。
开发好的插件还可以上传 Godot Asset Library 分享,只需要注册一个账户即可。上传资产时只需要登记相关的插件信息,下载链接地址,使用 Github 托管很容易处理,填写相应的 Commit ID 和预览图即可,然后等待审核通过后即可以在列表中搜索到,Feed 页面可以查看资产的审核状态。
C# 中可以自定义标注实现类似 GDScript 中的 classname 注册自定义类型:
- **System.Attribute** 自定义标注需要继承的基类;
- **System.AttributeUsage** 设置自定义标注的用途,以及 AllowMultiple,Inherited等属性;
- **System.AttributeTargets** 提供标注用途的枚举类型定义,如 Class, Method, Property 等等用途;
- [System.AttributeUsage(System.AttributeTargets.Class)]指定自定义标注作用于类型定义;
- [System.AttributeUsage(System.AttributeTargets.Method)]指定自定义标注作用于类型定义;
此外,System.Runtime.CompilerServices 命名空间下提供了一系列编译获取信息的标注,如:
- **CallerFilePathAttribute** 获取包含调用方的源文件的完整路径。 这是编译时的文件路径。
- **CallerLineNumberAttribute** 获取源文件中调用方法的行号。
- **CallerMemberNameAttribute** 获取方法调用方的方法或属性名称。
- **DateTimeConstantAttribute** 为字段或参数永久保存一个 8 字节的 DateTime 常数。
Caller 和 Callee 分别表示一个方法调用另一个方法的双方,Caller 是主动调用,Callee 是被调用方。向 Godot 注册自定义类型需要使用其脚本,需要使用 [CallerFilePath] 标注获取脚本路径,并通过资源管理器 ResourceLoader 加载它得到脚本对象。加载脚本得到的是一个资源类型,可以使用多种方式进行转换:
最新的 Godot 4 beta 16 提供了一个 GodotClassNameAttribute 标注,但它不是用来注册自定义类型。它用来在生成的 C# 代码中的类名不一致时指定引擎类名,以允许内省代码查找与类关联的名称。
C# 元数据编程中,一个方格号内可以包含多个标注,每个标注使用圆括号通过 = 运算符对其属性进行赋值。对标注好的代码进行处理时,使用程序集对象 Assembly 提供的反射方法,对当前运行中的程序集进行处理。通过处理程序集中标注过的类型,使用 AddCustomType() 将自定义类型注册到 Godot 系统内:
在 Godot 触发自定义节点注册行为,可以通过添加工具菜单 AddToolMenuItem() 来让用户操作,它会向 Project → Tools 添加菜单,需要在禁用插件配合 RemoveToolMenuItem() 做菜单清理,避免重复添加。也可以使用 EditorPlugin 的信号来触发,插件提供的信号包含场景变动、资源保存、工程配置变动等等。保存文件,包括各种资源文件,就会触发 **resource_saved** 信号。
插件中的自定义标注需要在 Godot 工程中其它代码引用,C# 工程文件 ``.csproj`` 要正确引用``.cs``,并且需要构建好 C# 项目使其生效。否则,启用插件时就会提示不能加载插件脚本,因为 Godot 不能通过脚本提供类型名称找到相应的程序集中对应的类型。
一个最简单的插件可以只包含两个方法,它向引擎注册自定义节点:
C# 版本插件类基本结构:
一个节点,包括插件,连接到 Root Viewport 即成为场景树中的一个部分,最后节点被移除,依次触发回调方法:
节点树所有节点会按位置先后、由表层到内层依次执行 enter_tree 方法,而 ready 和 exit_tree 方法,则不同,会先由内层到外层的顺序执行,父级节点需要等待内层节点工作完成才能执行 ready 等动作。
对于插件,Project → Project Settings → Plugins 面板中启用它,即添加到场景树上,禁用时即移除。插件代码如果有更新,也需要通过禁用再重新启用来更新,并重新执行初始化。Godot 4 beta 16 中,只要重新执行构建,插件功能即可以随之更新,不需要重新设置工程插件配置,但初始化在下次启用插件时执行。
以下是 CustomTypes 插件的实现,有两个 C# 代码文件,一个标注定义,另一个是主脚本。通过工程配置启用插件:Project → Project Settings... → Plugins → CustomTypes → Enable/Disable。
然后,插件会向 Project → Tools 工具菜单中注册两个功能,分别用于更新自定义类型的注册和清理:
- Project → Tools → C# Custom Types: Register
- Project → Tools → C# Custom Types: Clear
标注定义代码文件 ClassNameAttribute.cs 内容:
Custom Types 插件主脚本文件 Plugin.cs,在创建插件时将其填写到 Script Name 字段中:
参考:
- https://github.com/m50/Godot-CSharp-Node-Exports
- [.NET project SDKs](https://learn.microsoft.com/en-us/dotnet/core/project-sdk/overview)
- [Attribute 基类](https://learn.microsoft.com/zh-cn/dotnet/api/system.attribute?view=net-7.0)
- [CompilerServices 命名空间](https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.compilerservices)
- https://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/datetimeconstantattribute.cs
- https://docs.godotengine.org/en/3.5/tutorials/plugins/editor/index.html
- https://godotengine.org/asset-library/