GFramework/docs/zh-CN/godot/architecture.md
gewuyou 5778782df0 docs(godot): 收口旧文档口吻与采用说明
- 更新 Godot 与教程细页的 reader-facing 采用说明

- 修复旧文档、ai-libs 与内部术语在公开页面中的暴露

- 更新文档治理恢复点并记录接近阈值的停止状态
2026-04-27 09:02:48 +08:00

6.1 KiB
Raw Blame History

title, description
title description
Godot 架构集成 说明 AbstractArchitecture、ArchitectureAnchor 和 Godot 模块挂接的当前生命周期语义,以及推荐的初始化路径。

Godot 架构集成

概述

GFramework.Godot 当前的架构集成目标很直接:让 Architecture 能安全地感知 Godot SceneTree 生命周期,并在需要时把 带 Node 的扩展模块挂到场景树上。

当前真正参与这条链路的核心类型只有三类:

  • AbstractArchitecture:在原有 Architecture 之上增加 Godot 生命周期绑定
  • ArchitectureAnchor:挂在 SceneTree.Root 下的锚点节点,负责把 _ExitTree() 事件转回架构销毁
  • IGodotModule / AbstractGodotModule:当模块本身需要携带 Godot Node 时使用

它不是另一套独立的模块系统,也不意味着所有模块都必须改成 InstallGodotModule(...)

什么时候该用 AbstractArchitecture

当你的架构需要满足下面任一条件时,可以让它继承 AbstractArchitecture

  • 需要把架构生命周期绑定到 Godot SceneTree
  • 需要在架构里安装带 Node 的扩展模块
  • 需要通过受保护的 ArchitectureRoot 访问锚点节点,继续挂接 Godot 子节点

如果你只是做普通的 Model / System / Utility 注册,AbstractArchitecture 的主要价值仍然是“让架构知道自己何时跟随 Godot 场景树销毁”,而不是改变注册方式。

最小接入路径

常规模块仍然用 InstallModule(...)

当前更稳的默认做法,是保持普通模块注册方式:

using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Environment;
using GFramework.Godot.Architectures;

namespace MyGame.Scripts.Core;

public sealed class GameArchitecture(
    IArchitectureConfiguration configuration,
    IEnvironment environment)
    : AbstractArchitecture(configuration, environment)
{
    protected override void InstallModules()
    {
        InstallModule(new UtilityModule());
        InstallModule(new ModelModule());
        InstallModule(new GameplayModule());
        InstallModule(new SystemModule());
    }
}

这里继承 AbstractArchitecture 的意义,是把架构绑定到 Godot 生命周期,而不是把普通模块注册改写成 Godot 风格 API。

只有携带 Node 的模块才需要 InstallGodotModule(...)

如果模块本身暴露一个 Godot Node,并且希望由架构锚点统一托管,可以这样写:

using GFramework.Core.Abstractions.Architectures;
using GFramework.Godot.Architectures;
using Godot;

namespace MyGame.Scripts.Core;

public sealed class HudModule : AbstractGodotModule
{
    private readonly Control _root = new()
    {
        Name = "HudModule"
    };

    public override Node Node => _root;

    public override void Install(IArchitecture architecture)
    {
    }

    public override void OnAttach(GFramework.Core.Architectures.Architecture architecture)
    {
    }

    public override void OnDetach()
    {
        _root.QueueFree();
    }
}

这类模块的关键点不是“注册更多框架能力”,而是“让模块节点跟着架构锚点进出场景树”。 真正调用 InstallGodotModule(...) 时,也应该把它放在能够接受异步挂接流程的初始化路径里。

当前生命周期

初始化阶段

AbstractArchitecture.OnInitialize() 目前会按这个顺序工作:

  1. 生成唯一的锚点节点名称
  2. 调用 AttachToGodotLifecycle()
  3. 在可用的 SceneTree 上创建并绑定 ArchitectureAnchor
  4. 执行你重写的 InstallModules()

也就是说Godot 生命周期绑定先发生,业务模块注册后发生。

InstallGodotModule(...) 的执行顺序

当前实现里,InstallGodotModule(...) 会:

  1. 检查模块参数是否为 null
  2. 检查 _anchor 是否已初始化
  3. 先执行 module.Install(this)
  4. 把模块登记进内部 _extensions
  5. await anchor.WaitUntilReadyAsync()
  6. 通过 CallDeferred(AddChild, module.Node) 把模块节点挂到锚点下
  7. 调用 module.OnAttach(this)

这条顺序有两个实际意义:

  • 模块会在挂接节点前先完成框架侧注册
  • 只有等锚点真正 ready 后,才进入需要访问 Godot 节点 API 的附加阶段

销毁阶段

ArchitectureAnchor._ExitTree() 会触发绑定好的退出回调,随后 AbstractArchitecture 会开始观察异步销毁流程:

  • 防止重复销毁
  • 依次调用已登记 Godot 模块的 OnDetach()
  • 清空内部扩展列表
  • 再进入基类 DestroyAsync()

如果异步销毁抛异常,当前实现会把错误写到 Godot 错误输出,而不是静默吞掉。

当前边界

没有锚点时不会偷偷安装模块

GFramework.Godot.Tests/Architectures/AbstractArchitectureModuleInstallationTests.cs 已覆盖一个关键边界:

  • 当锚点尚未初始化时,InstallGodotModule(...) 会直接抛 InvalidOperationException("Anchor not initialized")
  • 失败发生在 module.Install(...) 之前,因此不会留下半安装副作用

这也是为什么 InstallGodotModule(...) 更适合放在异步初始化路径里,而不是同步阻塞地等待挂接结果。

AbstractGodotModule 只是便捷基类,不代表自动阶段广播

当前接口 IGodotModule 真正保证的成员只有:

  • Node
  • Install(IArchitecture architecture)
  • OnAttach(Architecture architecture)
  • OnDetach()

AbstractGodotModule 里虽然保留了 OnPhase(...) / OnArchitecturePhase(...) 虚方法,但它们不在当前接口契约内,也没有在 这条挂接流程里形成稳定的自动广播语义。不要把它写成当前公开保证。

ArchitectureRoot 只在锚点就绪后可用

ArchitectureRoot 是受保护属性,底层直接返回 _anchor。如果锚点尚未准备好或架构已经失效,它会抛 InvalidOperationException("Architecture root not ready")。因此它适合放在明确依赖锚点存在的挂接逻辑里,而不是拿来做 任意时机的全局节点查找。

继续阅读

  1. Godot 运行时集成
  2. Godot 集成教程
  3. Godot 场景系统
  4. Godot UI 系统