Node.queue_free
2026/4/14大约 3 分钟
最后同步日期:2026-04-16 | Godot 官方原文 — Node.queue_free
Node.queue_free
定义
queue_free 就像给一个节点贴上"待删除"标签——它不会立刻消失,而是排队等到当前这一帧(frame)的所有逻辑执行完之后才被销毁。这比直接调用 Free() 安全得多,因为不会在处理过程中突然把节点删掉导致其他代码崩溃。
这是游戏开发中最常用的节点销毁方式,比如子弹碰到敌人后消失、敌人血量归零后死亡、UI 弹窗关闭等。
函数签名
C#
public void QueueFree()GDScript
queue_free() -> void参数说明
无参数。
返回值
无返回值(void)。
代码示例
基础用法
最简单的用法——创建一个节点,然后排队删除它:
C#
public override void _Ready()
{
var tempNode = new Node();
tempNode.Name = "Temp";
AddChild(tempNode);
GD.Print(GetChildCount()); // 运行结果: 1
// 排队删除:不是立刻删除,而是等到当前帧结束
tempNode.QueueFree();
// 当前帧还没结束,节点仍然存在
GD.Print(GetChildCount()); // 运行结果: 1
// 等到下一帧时,节点才会被真正删除
}GDScript
func _ready():
var temp_node = Node.new()
temp_node.name = "Temp"
add_child(temp_node)
print(get_child_count()) # 运行结果: 1
# 排队删除:不是立刻删除,而是等到当前帧结束
temp_node.queue_free()
# 当前帧还没结束,节点仍然存在
print(get_child_count()) # 运行结果: 1
# 等到下一帧时,节点才会被真正删除实际场景
子弹碰到敌人后销毁(通过 Area2D 信号触发):
C#
public partial class Bullet : Area2D
{
[Export] public int ExDamage = 10;
public override void _Ready()
{
BodyEntered += OnBodyEntered;
}
private void OnBodyEntered(Node2D body)
{
// 如果碰到敌人
if (body.IsInGroup("enemies"))
{
// 对敌人造成伤害(省略具体实现)
GD.Print("命中敌人!造成 " + ExDamage + " 点伤害");
// 运行结果: 命中敌人!造成 10 点伤害
// 子弹命中后销毁自身
QueueFree();
}
}
}GDScript
extends Area2D
@export var damage: int = 10
func _ready():
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D):
# 如果碰到敌人
if body.is_in_group("enemies"):
# 对敌人造成伤害(省略具体实现)
print("命中敌人!造成 %d 点伤害" % damage)
# 运行结果: 命中敌人!造成 10 点伤害
# 子弹命中后销毁自身
queue_free()进阶用法
结合 IsQueuedForDeletion() 判断节点是否正在等待删除,以及使用定时器延迟删除:
C#
public partial class Enemy : CharacterBody2D
{
private bool _isDying = false;
public void TakeDamage(int amount)
{
// 如果已经在等死了,不要重复处理
if (_isDying) return;
GD.Print("受到 " + amount + " 点伤害!");
// 运行结果: 受到 30 点伤害!
_isDying = true;
// 播放死亡动画后再删除(用定时器模拟)
var timer = GetTree().CreateTimer(1.0);
timer.Timeout += () =>
{
GD.Print("敌人被消灭!");
// 运行结果: 敌人被消灭!
QueueFree();
};
}
public override void _Process(double delta)
{
// 检查节点是否正在排队等待删除
if (IsQueuedForDeletion())
{
GD.Print("正在等待被删除...");
// 运行结果: 正在被删除...
}
}
}GDScript
extends CharacterBody2D
var _is_dying = false
func take_damage(amount: int):
# 如果已经在等死了,不要重复处理
if _is_dying:
return
print("受到 %d 点伤害!" % amount)
# 运行结果: 受到 30 点伤害!
_is_dying = true
# 播放死亡动画后再删除(用定时器模拟)
var timer = get_tree().create_timer(1.0)
await timer.timeout
print("敌人被消灭!")
# 运行结果: 敌人被消灭!
queue_free()
func _process(delta):
# 检查节点是否正在排队等待删除
if is_queued_for_deletion():
print("正在等待被删除...")
# 运行结果: 正在被删除...注意事项
QueueFree()vsFree():QueueFree()是延迟删除(等当前帧结束),Free()是立即删除。绝大多数情况下用QueueFree(),在_Process或_PhysicsProcess中使用Free()会导致崩溃,因为引擎还在遍历节点。- 同一帧内多次调用 `QueueFree()`` 安全:同一个节点即使被多次标记删除也不会出错。
_ExitTree()会被调用:节点真正被删除时,_ExitTree()回调会执行,可以在这里做清理工作。- 子节点也会一起被删除:调用父节点的
QueueFree()时,所有子节点也会被一并销毁。 - C# 差异:C# 中用 PascalCase(
QueueFree()),GDScript 中用 snake_case(queue_free())。
