- 更新 godot signal 页面,明确 Signal(...)、SignalBuilder 与 [BindNodeSignal] 的分工 - 更新 godot extensions 页面,收敛到当前存在的扩展成员与生命周期边界 - 补充 documentation-governance-and-refresh 跟踪与 trace,记录 RP-016 和验证结果
5.7 KiB
title, description
| title | description |
|---|---|
| Godot 扩展方法 | 以当前 GFramework.Godot.Extensions 源码为准,说明路径、Node、signal 和 unregister 扩展的真实成员与边界。 |
Godot 扩展方法
GFramework.Godot.Extensions 当前并不是“覆盖所有 Godot 节点操作”的万能层。按源码看,它实际公开的扩展主要只有四组:
GodotPathExtensionsNodeExtensionsSignalFluentExtensionsUnRegisterExtension
这页的重点应该是识别这些扩展各自解决什么问题,以及哪些旧文档里的“大而全能力”现在并不存在。
当前公开入口
GodotPathExtensions
这组扩展只负责判断 Godot 虚拟路径前缀:
IsUserPath(this string path)IsResPath(this string path)IsGodotPath(this string path)
它们不做文件访问,也不解析目录结构,只是用字符串前缀判断 user:// 和 res://。
using GFramework.Godot.Extensions;
if ("user://save.json".IsUserPath())
{
}
if ("res://config/gameplay.yaml".IsGodotPath())
{
}
NodeExtensions
NodeExtensions 是当前扩展集合里体量最大的部分,但职责仍然比较具体,主要分成下面几类。
生命周期与有效性辅助
QueueFreeX(this Node? node)FreeX(this Node? node)WaitUntilReadyAsync(this Node node)WaitUntilReady(this Node node, Action callback)IsValidNode(this Node? node)IsInvalidNode(this Node? node)
这里最容易写偏的地方有两个:
QueueFreeX()/FreeX()会先检查 null、实例是否仍有效、是否已经进入删除队列IsValidNode()不只要求实例还活着,还要求节点已经在SceneTree里;单纯new出来但还没挂树的节点会返回false
节点访问与装配辅助
FindChildX<T>(...)GetOrCreateNode<T>(...)AddChildXAsync(...)GetParentX<T>()GetRootNodeX()ForEachChild<T>(...)OfType<T>()
这几组方法更偏“少量常用装配动作”,不是完整查询 DSL。
特别是 GetOrCreateNode<T>(string path) 的当前实现要注意:
- 先尝试
GetNodeOrNull<T>(path) - 如果没找到,就
new T() - 把新节点直接
AddChild(...)到当前节点 - 再把
created.Name = path
它不会按斜杠路径逐级创建中间节点,所以不要把它当成层级化路径构建器。
输入、暂停与调试辅助
SetInputAsHandled()Paused(bool paused = true)DisableInput()EnableInput()LogNodePath()PrintTreeX(string indent = "")SafeCallDeferred(string method)
这些方法都很薄,基本是在现有 Viewport / SceneTree / CallDeferred(...) 上做便捷包装,没有额外状态机。
SignalFluentExtensions
SignalFluentExtensions 只提供一个入口:
Signal(this GodotObject @object, StringName signal)
它把目标对象和 signal 名称包装成 SignalBuilder。具体连接语义请看 Godot 信号系统。
UnRegisterExtension
UnRegisterExtension 当前也只有一个公开方法:
UnRegisterWhenNodeExitTree(this IUnRegister unRegister, Node node)
它做的事情很明确:把 unRegister.UnRegister 挂到 node.TreeExiting 上。这样框架侧的订阅句柄就能跟 Godot 节点生命周期对齐。
IUnRegister subscription = eventBus.Subscribe<SettingsChangedEvent>(OnSettingsChanged);
subscription.UnRegisterWhenNodeExitTree(this);
它不会接管普通 Godot signal 的断开逻辑,也不会帮你推断别的释放时机。
最小接入路径
1. 节点进入树之后再做装配
如果你的节点可能在 _Ready() 前就被访问,先用 WaitUntilReadyAsync():
using GFramework.Godot.Extensions;
using GFramework.Godot.Extensions.Signal;
using Godot;
public partial class SettingsPanel : Control
{
public override async void _Ready()
{
await this.WaitUntilReadyAsync();
var applyButton = FindChildX<Button>("ApplyButton");
applyButton?.Signal(Button.SignalName.Pressed)
.To(Callable.From(OnApplyPressed));
}
private void OnApplyPressed()
{
this.SetInputAsHandled();
}
}
2. 框架订阅和节点生命周期一起收尾
当订阅句柄实现了 IUnRegister,可以把释放时机绑到节点退出树:
public override void _Ready()
{
IUnRegister subscription = _eventBus.Subscribe<SettingsChangedEvent>(OnSettingsChanged);
subscription.UnRegisterWhenNodeExitTree(this);
}
这比在多个 _ExitTree() / Dispose() 分支里手写解绑更稳定,也更符合当前扩展的职责边界。
3. 只在需要时使用 signal fluent API
Signal(...) 属于扩展集合的一部分,但它已经有独立页面。实践上可以这样分工:
- 节点查找、ready 等待、输入处理:
NodeExtensions - 动态 signal 绑定:
Signal(...) - 框架订阅释放:
UnRegisterWhenNodeExitTree(...) - 路径前缀判断:
GodotPathExtensions
当前边界
- 当前
NodeExtensions没有GetNodeX()、CreateSignalBuilder()之类旧文档里提过的 API - 它不是 router、scene factory、UI factory 或生成器的替代层
GetOrCreateNode<T>()只会创建一个直接子节点,不会递归补整条路径SafeCallDeferred(...)只有在IsValidNode()为true时才会调用;节点未入树时不会执行UnRegisterWhenNodeExitTree(...)只针对实现了IUnRegister的框架订阅句柄,不会自动处理 Godot 原生Connect(...)- 协程辅助扩展在
GFramework.Godot.Coroutine命名空间,不属于这组Extensions页面要覆盖的核心范围