using Godot;
namespace GFramework.Core.Godot.extensions;
///
/// 节点扩展方法类,提供对Godot节点的扩展功能
///
public static class NodeExtensions
{
///
/// 安全地将节点加入删除队列,在下一帧开始时释放节点资源
///
/// 要释放的节点实例
public static void QueueFreeX(this Node? node)
{
// 检查节点是否为空
if (node is null) return;
// 检查节点实例是否有效
if (!GodotObject.IsInstanceValid(node)) return;
// 检查节点是否已经加入删除队列
if (node.IsQueuedForDeletion()) return;
// 延迟调用QueueFree方法,避免在当前帧中直接删除节点
node.CallDeferred(Node.MethodName.QueueFree);
}
///
/// 立即释放节点资源,不等待下一帧
///
/// 要立即释放的节点实例
public static void FreeX(this Node? node)
{
// 检查节点是否为空
if (node is null) return;
// 检查节点实例是否有效
if (!GodotObject.IsInstanceValid(node)) return;
// 检查节点是否已经加入删除队列
if (node.IsQueuedForDeletion()) return;
// 立即释放节点资源
node.Free();
}
///
/// 如果节点尚未进入场景树,则等待 ready 信号。
/// 如果已经在场景树中,则立刻返回。
///
public static async Task WaitUntilReady(this Node node)
{
if (!node.IsInsideTree()) await node.ToSignal(node, Node.SignalName.Ready);
}
///
/// 检查节点是否有效:
/// 1. 非 null
/// 2. Godot 实例仍然存在(未被释放)
/// 3. 已经加入 SceneTree
///
public static bool IsValidNode(this Node? node)
{
return node is not null &&
GodotObject.IsInstanceValid(node) &&
node.IsInsideTree();
}
///
/// 检查节点是否无效:
/// 1. 为 null,或者
/// 2. Godot 实例已被释放,或者
/// 3. 尚未加入 SceneTree
/// 返回 true 表示该节点不可用。
///
public static bool IsInvalidNode(this Node? node)
{
return node is null ||
!GodotObject.IsInstanceValid(node) ||
!node.IsInsideTree();
}
///
/// 将当前节点的输入事件标记为已处理,防止事件继续向父节点传播。
///
/// 要处理输入事件的节点实例
public static void SetInputAsHandled(this Node node)
{
// 获取节点的视口并标记输入事件为已处理
node.GetViewport().SetInputAsHandled();
}
///
/// 设置节点所在场景树的暂停状态
///
/// 要操作的节点对象
/// 暂停状态标识,默认为true表示暂停,false表示恢复运行
public static void Paused(this Node node, bool paused = true)
{
var tree = node.GetTree();
tree.Paused = paused;
}
///
/// 查找指定名称的子节点并将其转换为指定类型
///
/// 要转换到的目标节点类型
/// 要在其子节点中进行查找的父节点
/// 要查找的子节点名称
/// 是否递归查找所有层级的子节点,默认为true
/// 找到的子节点转换为指定类型后的结果,如果未找到或转换失败则返回null
public static T? FindChildX(this Node node, string name, bool recursive = true)
where T : Node
{
var child = node.FindChild(name, recursive, false);
return child as T;
}
///
/// 获取指定路径的节点,如果不存在则创建一个新的节点
///
/// 节点类型,必须继承自Node且具有无参构造函数
/// 父节点
/// 节点路径
/// 找到的现有节点或新创建的节点
public static T GetOrCreateNode(this Node node, string path)
where T : Node, new()
{
// 尝试获取现有节点
if (node.GetNodeOrNull(path) is { } found)
return found;
// 创建新节点并添加到父节点
var created = new T();
node.AddChild(created);
created.Name = path;
return created;
}
///
/// 异步添加子节点并等待其准备就绪
///
/// 父节点
/// 要添加的子节点
/// 异步任务
public static async Task AddChildX(this Node parent, Node child)
{
parent.AddChild(child);
await child.WaitUntilReady();
}
///
/// 获取父节点并将其转换为指定类型
///
/// 要转换到的目标节点类型
/// 当前节点
/// 父节点转换为指定类型后的结果,如果转换失败则返回null
public static T? GetParentX(this Node node) where T : Node
{
return node.GetParent() as T;
}
///
/// 获取场景树的根节点的第一个子节点
///
/// 扩展方法的目标节点
/// 根节点的第一个子节点
public static Node GetRootNodeX(this Node node)
{
return node.GetTree().Root.GetChild(0);
}
///
/// 遍历节点的所有子节点,并对指定类型的子节点执行特定操作
///
/// 要筛选的节点类型
/// 扩展方法的目标节点
/// 对符合条件的子节点执行的操作
public static void ForEachChild(this Node node, Action action) where T : Node
{
foreach (var child in node.GetChildren())
if (child is T t)
action(t);
}
///
/// 禁用节点所在场景树的输入处理功能
///
/// 扩展方法的目标节点
public static void DisableInput(this Node node)
{
// 检查根节点是否为Viewport类型,如果是则禁用GUI输入
if (node.GetTree().Root is Viewport vp)
vp.GuiDisableInput = true;
}
///
/// 启用节点所在场景树的输入处理功能
///
/// 扩展方法的目标节点
public static void EnableInput(this Node node)
{
// 检查根节点是否为Viewport类型,如果是则启用GUI输入
if (node.GetTree().Root is Viewport vp)
vp.GuiDisableInput = false;
}
///
/// 打印节点的路径信息到控制台
///
/// 扩展方法的目标节点
public static void LogNodePath(this Node node)
{
GD.Print($"[NodePath] {node.GetPath()}");
}
///
/// 以树形结构递归打印节点及其所有子节点的名称
///
/// 扩展方法的目标节点
/// 缩进字符串,用于显示层级关系
public static void PrintTreeX(this Node node, string indent = "")
{
GD.Print($"{indent}- {node.Name}");
// 递归打印所有子节点
foreach (var child in node.GetChildren())
child.PrintTreeX(indent + " ");
}
///
/// 安全地延迟调用指定方法,确保节点有效后再执行
///
/// 扩展方法的目标节点
/// 要延迟调用的方法名
public static void SafeCallDeferred(this Node? node, string method)
{
// 检查节点是否为空且实例是否有效,有效时才执行延迟调用
if (node != null && GodotObject.IsInstanceValid(node))
node.CallDeferred(method);
}
}