is_instance_id_valid
2026/4/14大约 5 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — is_instance_id_valid
is_instance_id_valid
定义
is_instance_id_valid() 用于检查给定的实例 ID 是否对应一个仍然存在的、有效的对象。
打个生活中的比方:这就像查快递单号。你手上有快递单号(实例 ID),想知道这个单号对应的包裹是不是还在。如果包裹已经被签收或退回了(对象被释放了),那这个单号就"失效"了。is_instance_id_valid() 就是帮你查"这个单号还有没有效"的工具。
在 Godot 中,每个对象(节点、资源等)在创建时都会获得一个唯一的实例 ID。当一个对象被销毁(free() 或 queue_free())后,它的实例 ID 就不再指向任何东西了。这个函数可以帮你在使用一个 ID 之前,先确认它是否还有效,从而避免程序崩溃。
函数签名
C#
// GodotObject.IsInstanceIdValid — 检查实例 ID 是否对应有效对象
// 位于 GodotObject 类中的静态方法
public static bool IsInstanceIdValid(ulong id)
// 另一种写法:通过 InstanceId 结构体的辅助方法
//InstanceId.HasInstance(id) // 需要 using Godot;GDScript
# is_instance_id_valid — 检查实例 ID 是否对应有效对象
func is_instance_id_valid(id: int) -> bool参数说明
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
id | int(GDScript)/ ulong(C#) | 是 | 要检查的实例 ID。每个 Godot 对象都有一个唯一的数字 ID,可以通过 obj.get_instance_id() 获取 |
什么是实例 ID? 你可以把它理解为 Godot 给每个对象分配的"身份证号"。只要对象存在,这个号就能找到它;对象被销毁后,这个号就作废了。
返回值
返回一个 bool(布尔值):
true:该 ID 对应的对象仍然存在且有效,可以安全使用false:该 ID 对应的对象已被释放(调用了free()或queue_free()),或者这个 ID 从来就不对应任何对象
代码示例
基本用法:检查对象是否还存在
C#
using Godot;
public partial class InstanceIdExample : Node
{
public override void _Ready()
{
// 创建一个节点
var myNode = new Node();
AddChild(myNode);
// 记录它的实例 ID
ulong savedId = myNode.GetInstanceId();
GD.Print($"节点的实例 ID: {savedId}");
// 此时 ID 是有效的
GD.Print($"ID 有效吗? {GodotObject.IsInstanceIdValid(savedId)}"); // true
// 销毁节点
myNode.QueueFree();
// 注意:QueueFree 是延迟销毁,当前帧内 ID 仍然"暂时有效"
// 等到下一帧,对象才真正被销毁
}
public override void _Process(double delta)
{
// 在后续帧中检查(演示用,实际使用时需保存 savedId)
// 如果对象已被销毁,ID 将失效
}
}GDScript
extends Node
func _ready():
# 创建一个节点
var my_node = Node.new()
add_child(my_node)
# 记录它的实例 ID
var saved_id = my_node.get_instance_id()
print("节点的实例 ID: ", saved_id)
# 此时 ID 是有效的
print("ID 有效吗? ", is_instance_id_valid(saved_id)) # true
# 销毁节点
my_node.queue_free()
# 注意:queue_free 是延迟销毁,当前帧内 ID 仍然"暂时有效"
# 等到下一帧,对象才真正被销毁实战场景:安全地使用延迟保存的实例 ID
在多人游戏或复杂系统中,你可能会把某个对象的实例 ID 保存下来,稍后再用。使用前先检查有效性是一个好习惯。
C#
using Godot;
using System.Collections.Generic;
public partial class TargetManager : Node
{
// 内部变量:保存的目标 ID 列表
private readonly List<ulong> _targetIds = new();
// 记录一个目标
public void RegisterTarget(Node target)
{
_targetIds.Add(target.GetInstanceId());
}
// 检查并清理所有已失效的目标
public void CleanupInvalidTargets()
{
_targetIds.RemoveAll(id => !GodotObject.IsInstanceIdValid(id));
GD.Print($"剩余有效目标数量: {_targetIds.Count}");
}
// 尝试根据 ID 获取对象(安全方式)
public Node TryGetTarget(ulong id)
{
if (!GodotObject.IsInstanceIdValid(id))
{
GD.PrintErr("目标 ID 已失效,对象已被销毁");
return null;
}
return GodotObject.InstanceFromId(id) as Node;
}
public override void _Process(double delta)
{
// 每隔一段时间清理失效目标
CleanupInvalidTargets();
}
}GDScript
extends Node
# 内部变量:保存的目标 ID 列表
var _target_ids: Array[int] = []
# 记录一个目标
func register_target(target: Node) -> void:
_target_ids.append(target.get_instance_id())
# 检查并清理所有已失效的目标
func cleanup_invalid_targets() -> void:
var valid_ids: Array[int] = []
for id in _target_ids:
if is_instance_id_valid(id):
valid_ids.append(id)
_target_ids = valid_ids
print("剩余有效目标数量: ", _target_ids.size())
# 尝试根据 ID 获取对象(安全方式)
func try_get_target(id: int) -> Node:
if not is_instance_id_valid(id):
push_error("目标 ID 已失效,对象已被销毁")
return null
return instance_from_id(id) as Node
func _process(_delta: float) -> void:
# 每隔一段时间清理失效目标
cleanup_invalid_targets()注意事项
queue_free()是延迟销毁:调用queue_free()后,对象并不是立刻消失,而是在当前帧结束后才被销毁。所以在同一帧内,is_instance_id_valid()仍然会返回true。如果你需要立刻销毁,可以使用free(),但要非常小心——free()是立即生效的,稍有不慎就会访问已释放的内存。- 与
instance_from_id()搭配使用:instance_from_id()可以根据实例 ID 拿回原始对象。但在调用之前,最好先用is_instance_id_valid()确认 ID 仍然有效,否则可能拿到null或导致程序崩溃。 - C# 中的替代写法:C# 中除了
GodotObject.IsInstanceIdValid(ulong id),还可以使用GodotObject.InstanceFromId(id)并检查返回值是否为null,效果类似。选择哪种方式取决于你的编码风格。 - 实例 ID 是全局唯一的:每个对象在创建时获得一个唯一的 ID,即使对象被销毁了,这个 ID 也不会被重复分配给新对象(至少在当前运行会话中)。所以你不用担心"旧 ID 被新对象占用"的问题。
- ID 为 0 表示无效:如果传入的 ID 为 0,函数会返回
false,因为 0 不是有效的实例 ID。
