diff --git a/GFramework.Core/architecture/Architecture.cs b/GFramework.Core/architecture/Architecture.cs index 16d07b6..885a916 100644 --- a/GFramework.Core/architecture/Architecture.cs +++ b/GFramework.Core/architecture/Architecture.cs @@ -177,7 +177,7 @@ public abstract class Architecture : IArchitecture where T : Architecture, /// 此函数负责有序地销毁架构中的所有系统组件,并发送相应的生命周期事件。 /// 函数会确保只执行一次销毁操作,避免重复销毁。 /// - public void Destroy() + public virtual void Destroy() { // 检查当前阶段,如果已经处于销毁或已销毁状态则直接返回 if (CurrentPhase >= ArchitecturePhase.Destroying) diff --git a/GFramework.Godot/architecture/AbstractArchitecture.cs b/GFramework.Godot/architecture/AbstractArchitecture.cs index e30cd4e..e02546f 100644 --- a/GFramework.Godot/architecture/AbstractArchitecture.cs +++ b/GFramework.Godot/architecture/AbstractArchitecture.cs @@ -1,33 +1,66 @@ using GFramework.Core.architecture; +using GFramework.Godot.extensions; using Godot; namespace GFramework.Godot.architecture; /// -/// 抽象架构类,为特定类型的架构提供基础实现框架 +/// 抽象架构类,为特定类型的架构提供基础实现框架。 +/// 此类负责管理架构的初始化、生命周期绑定以及扩展模块的安装与销毁。 /// -/// 架构的具体类型,必须继承自Architecture且能被实例化 +/// 架构的具体类型,必须继承自Architecture且能被实例化。 public abstract class AbstractArchitecture : Architecture where T : Architecture, new() { + /// + /// 架构锚点节点的唯一标识名称 + /// 用于在Godot场景树中创建和查找架构锚点节点 + /// private const string ArchitectureName = "__GFramework__Architecture__Anchor"; /// - /// 初始化架构,按顺序注册模型、系统和工具 + /// 存储所有已安装的Godot架构扩展组件列表 + /// 用于在架构销毁时正确清理所有扩展资源 + /// + private readonly List> _extensions = []; + + /// + /// 架构锚点节点引用 + /// 用于将架构绑定到Godot生命周期并作为扩展节点的父节点 + /// + private ArchitectureAnchorNode? _anchor; + + /// + /// 获取架构根节点。如果尚未初始化或已被销毁,则抛出异常。 + /// + /// 当架构未准备就绪时抛出。 + protected Node ArchitectureRoot => _anchor ?? throw new InvalidOperationException("Architecture root not ready"); + + /// + /// 标记架构是否已被销毁的状态标志 + /// 用于防止架构被重复销毁,确保资源清理只执行一次 + /// + private bool _destroyed; + + + /// + /// 初始化架构,按顺序注册模型、系统和工具。 + /// 包括将架构绑定到Godot生命周期并调用模块安装逻辑。 /// protected override void Init() { AttachToGodotLifecycle(); InstallModules(); } - + /// - /// 安装模块抽象方法,由子类实现具体的模块注册逻辑 + /// 安装模块抽象方法,由子类实现具体的模块注册逻辑。 + /// 子类应在此方法中完成所有模型、系统及工具的注册工作。 /// protected abstract void InstallModules(); - + /// - /// 将架构绑定到Godot生命周期中,确保在场景树销毁时能够正确清理资源 - /// 通过创建一个锚节点来监听场景树的销毁事件 + /// 将架构绑定到Godot生命周期中,确保在场景树销毁时能够正确清理资源。 + /// 通过创建一个锚节点来监听场景树的销毁事件。 /// private void AttachToGodotLifecycle() { @@ -38,14 +71,58 @@ public abstract class AbstractArchitecture : Architecture where T : Archit if (tree.Root.GetNodeOrNull(ArchitectureName) != null) return; - var anchor = new ArchitectureAnchorNode + _anchor = new ArchitectureAnchorNode { Name = ArchitectureName }; - anchor.Bind(Destroy); + _anchor.Bind(Destroy); - tree.Root.CallDeferred(Node.MethodName.AddChild, anchor); + tree.Root.CallDeferred(Node.MethodName.AddChild, _anchor); } -} + + /// + /// 安装Godot架构扩展组件 + /// + /// 要安装的Godot架构扩展实例,必须实现IGodotArchitectureExtension接口 + /// 异步任务,表示扩展安装过程 + protected async Task InstallGodotExtension(IGodotArchitectureExtension extension) + { + // 检查锚点是否已初始化,未初始化则抛出异常 + if (_anchor == null) + throw new InvalidOperationException("Anchor not initialized"); + + // 等待锚点准备就绪 + await _anchor.WaitUntilReady(); + + // 延迟调用将扩展节点添加为锚点的子节点 + _anchor.CallDeferred(Node.MethodName.AddChild, extension.Node); + + // 调用扩展的附加回调方法 + extension.OnAttach(this); + + // 将扩展添加到扩展集合中 + _extensions.Add(extension); + } + + + /// + /// 销毁架构及其相关资源。 + /// 调用所有已安装扩展的OnDetach方法,并清空扩展列表。 + /// 若已被销毁则直接返回。 + /// + public override void Destroy() + { + if (_destroyed) + return; + + _destroyed = true; + foreach (var ext in _extensions) + ext.OnDetach(); + + _extensions.Clear(); + + base.Destroy(); + } +} \ No newline at end of file diff --git a/GFramework.Godot/architecture/ArchitectureAnchorNode.cs b/GFramework.Godot/architecture/ArchitectureAnchorNode.cs index 59e739c..ca1c0f4 100644 --- a/GFramework.Godot/architecture/ArchitectureAnchorNode.cs +++ b/GFramework.Godot/architecture/ArchitectureAnchorNode.cs @@ -9,13 +9,17 @@ namespace GFramework.Godot.architecture; public partial class ArchitectureAnchorNode : Node { private Action? _onExit; - /// /// 绑定节点退出时的回调动作 /// /// 当节点从场景树退出时要执行的动作 public void Bind(Action onExit) { + if (_onExit != null) + { + GD.PushWarning( + $"{nameof(ArchitectureAnchorNode)} already bound. Rebinding will override previous callback."); + } _onExit = onExit; } @@ -25,10 +29,9 @@ public partial class ArchitectureAnchorNode : Node /// public override void _ExitTree() { - // 执行退出回调 - _onExit?.Invoke(); - // 清理引用 + var callback = _onExit; _onExit = null; + callback?.Invoke(); } } diff --git a/GFramework.Godot/architecture/IGodotArchitectureExtension.cs b/GFramework.Godot/architecture/IGodotArchitectureExtension.cs new file mode 100644 index 0000000..70a69e3 --- /dev/null +++ b/GFramework.Godot/architecture/IGodotArchitectureExtension.cs @@ -0,0 +1,11 @@ +using GFramework.Core.architecture; +using Godot; + +namespace GFramework.Godot.architecture; + +public interface IGodotArchitectureExtension where T : Architecture, new() +{ + Node Node { get; } + void OnAttach(Architecture architecture); + void OnDetach(); +}