GFramework/docs/zh-CN/source-generators/get-node-generator.md
gewuyou 3c0ac1858a docs(batch-boot): 收口公开文档中的内部参考路径
- 更新 Game、Godot 与 source-generators 多个专题页的 reader-facing 示例口吻

- 移除 ai-libs CoreGrid 路径在公开页面中的直接暴露,保留项目侧常见实现说明

- 补充 documentation full coverage active topic 的 RP-046 跟踪与验证记录
2026-04-28 07:37:19 +08:00

5.9 KiB
Raw Blame History

title, description
title description
GetNode 生成器 说明 [GetNode] 当前生成什么、路径如何推断,以及 _Ready 生命周期里的接入边界。

GetNode 生成器

[GetNode] 用来把 Godot 节点查找样板收敛到生成器里。它只处理“字段如何取到节点”,不负责事件订阅,也不负责其他运行时装配。

当前包关系

  • 特性来源:GFramework.Godot.SourceGenerators.Abstractions
  • 生成器实现:GFramework.Godot.SourceGenerators
  • 使用前提:字段类型必须继承 Godot.Node

最小用法

using GFramework.Godot.SourceGenerators.Abstractions;
using Godot;

public partial class TopBar : HBoxContainer
{
    [GetNode]
    private HBoxContainer _leftContainer = null!;

    [GetNode]
    private HBoxContainer m_rightContainer = null!;
}

如果目标类型还没有 _Ready(),当前生成器会补出:

private void __InjectGetNodes_Generated()
{
    _leftContainer = GetNode<global::Godot.HBoxContainer>("%LeftContainer");
    m_rightContainer = GetNode<global::Godot.HBoxContainer>("%RightContainer");
}

partial void OnGetNodeReadyGenerated();

public override void _Ready()
{
    __InjectGetNodes_Generated();
    OnGetNodeReadyGenerated();
}

这个行为来自当前生成器测试,不是文档约定。

当前路径推断规则

没写路径时

如果 [GetNode] 没有显式路径,当前默认按字段名推导唯一名路径:

  • _leftContainer -> %LeftContainer
  • m_rightContainer -> %RightContainer

也就是说,默认不是普通相对路径,而是 Godot 的 %Name 唯一名语法。

显式路径优先

[GetNode("ScoreContainer/ScoreValue")]
private Label _scoreLabel = null!;

显式路径会直接进入生成结果,不再按字段名推断。

LookupRequired 的当前语义

Lookup

GetNodeAttribute.Lookup 支持 4 个模式:

  • Auto
  • UniqueName
  • RelativePath
  • AbsolutePath

对文档来说,最关键的结论是:

  • Auto 在未给路径时默认走唯一名推断
  • 一旦显式给了 Path,生成结果直接使用这个字符串,Lookup 不再改写它

可以直接按下面这张表理解当前行为:

Path Lookup 生成路径
未设置 Auto %FieldName
未设置 UniqueName %FieldName
未设置 RelativePath FieldName
未设置 AbsolutePath /FieldName
已显式设置 任意值 原样使用显式路径

例如:

[GetNode("HUD/ScoreLabel", Lookup = NodeLookupMode.AbsolutePath)]
private Label _scoreLabel = null!;

当前生成结果仍然会直接使用 "HUD/ScoreLabel",不会因为 Lookup = AbsolutePath 被改写成 "/HUD/ScoreLabel"

Required

默认 Required = true,生成器会调用 GetNode<T>()

[GetNode]
private Label _title = null!;

如果设为 false,生成器会改用 GetNodeOrNull<T>()

[GetNode(Required = false, Lookup = NodeLookupMode.RelativePath)]
private HBoxContainer? _rightContainer;

当前生成结果会是:

_rightContainer = GetNodeOrNull<global::Godot.HBoxContainer>("RightContainer");

所以可选节点最好同时用可空字段类型表达你的意图。

生命周期边界

没有 _Ready()

生成器会补:

  • __InjectGetNodes_Generated()
  • partial void OnGetNodeReadyGenerated()
  • 一个 public override void _Ready()

OnGetNodeReadyGenerated() 只在这种“生成器自己补 _Ready()”的路径里出现。

已经有 _Ready()

如果类型已经实现了 _Ready(),生成器不会覆盖它,也不会再额外生成 OnGetNodeReadyGenerated()。你必须自己调用:

public override void _Ready()
{
    __InjectGetNodes_Generated();
}

如果 _Ready() 存在但没有调用生成方法,当前会给出 warning提醒你手动接入。

当前强约束

这些约束都直接来自生成器源码和测试:

  • 目标类型必须是顶层 partial class
  • 不支持嵌套类
  • 字段必须是实例字段
  • 字段不能是 readonly
  • 字段类型必须继承 Godot.Node
  • 如果无法从字段名或显式参数推断出路径,会报错
  • 如果你自己定义了 __InjectGetNodes_Generated(),会触发命名冲突诊断

与 BindNodeSignal 的配合顺序

如果同一个类型同时用了 [GetNode][BindNodeSignal],当前推荐顺序是:

public override void _Ready()
{
    __InjectGetNodes_Generated();
    __BindNodeSignals_Generated();
}

先注入节点,再绑定事件;否则 BindNodeSignal 对应的字段还没完成解析。

这也是项目侧节点类的常见接法。

什么时候适合用 [GetNode]

适合:

  • 节点字段很多,GetNode<T>() 样板明显重复
  • 你希望把“字段名到节点路径”的约定收敛到声明式特性
  • 你在 Godot ControlNodeCanvasLayer 等项目侧类型上频繁访问子节点

不适合:

  • 目标不是 Godot.Node
  • 节点路径完全动态,必须在运行时决定
  • 你需要更复杂的节点查找策略,而不是字段级静态描述

与旧写法的边界

下面这些旧理解已经不准确:

  • [GetNode] 总会自动帮你改写 _Ready()
  • “不管是否已有 _Ready(),都会生成 OnGetNodeReadyGenerated()
  • “可选节点只是文档建议,生成结果不会变”

当前更准确的理解是:

  • 只有缺少 _Ready() 时才会自动补 override
  • OnGetNodeReadyGenerated() 只存在于自动补 _Ready() 的路径
  • Required = false 会真实切换到 GetNodeOrNull<T>()
  • Lookup 只影响“未显式给路径时”的推断前缀;显式 Path 不会被二次改写

推荐阅读

  1. BindNodeSignal 生成器
  2. Godot 项目生成器
  3. Godot UI 系统
  4. Godot 模块总览