在 Unity 开发中,查找物体的方法有很多,主要分为 按名称查找、按标签查找、按类型查找、按父子层级查找、按组件查找、按场景查找、通过引用查找 等。
一、按名称查找物体
1. GameObject.Find()
根据物体名称查找场景中的 GameObject,返回第一个匹配的对象。
GameObject player = GameObject.Find("Player");
假设场景中有一个名为 Player 的物体,就可以通过名称找到它。
特点:
- 通过 GameObject 的名称查找。
- 只查找当前已加载场景中处于激活状态的物体。
- 如果没有找到,返回
null。 - 同名物体可能导致查找结果不符合预期。
使用场景: 临时查找场景中的特定物体,例如 Player、Main Camera。
注意:GameObject.Find() 不适合在 Update() 中反复调用,因为每次查找都需要进行搜索。
2. Transform.Find()
在指定 Transform 的子层级中,按名称查找子物体。
Transform child = transform.Find("Weapon");
例如,层级结构如下:
Player
└── Weapon
└── Gun
如果脚本挂在 Player 上,transform.Find("Weapon") 就可以找到 Weapon。
也可以使用路径:
Transform gun = transform.Find("Weapon/Gun");
特点:
- 从当前 Transform 开始查找指定子层级。
- 支持使用
/表示层级路径。 - 不会递归搜索所有后代中的任意同名物体;路径需要与层级结构匹配。
- 不适合用来查找场景中任意位置的物体。
使用场景: 查找角色武器、UI 子节点、特效挂点等。
3. transform.GetChild()
根据子物体的索引获取子物体。
Transform child = transform.GetChild(0);
例如:
Panel
├── Button (索引 0)
├── Image (索引 1)
└── Text (索引 2)
transform.GetChild(0) 获取第一个直接子物体。
特点:
- 通过索引获取,不需要知道名称。
- 只获取直接子物体,不会自动深入孙级节点。
- 索引从
0开始。 - 索引超出范围会抛出异常。
使用场景: 遍历背包格子、动态生成的列表、Tilemap 相关层级节点。
for (int i = 0; i < transform.childCount; i++)
{
Transform child = transform.GetChild(i);
}
二、按标签查找物体
4. GameObject.FindWithTag()
根据 Tag 查找一个匹配的激活物体。
GameObject player = GameObject.FindWithTag("Player");
使用前需要在 Inspector 中给物体设置对应的 Tag。
特点:
- 根据标签查找,不依赖物体名称。
- 只返回一个匹配对象。
- 找不到时返回
null。 - 如果有多个物体具有相同 Tag,不应依赖它返回哪一个。
使用场景: 查找玩家、主摄像机以外的特定目标、敌人或出生点。
5. GameObject.FindGameObjectWithTag()
GameObject enemy =
GameObject.FindGameObjectWithTag("Enemy");
这是 FindWithTag() 的同义 API,用法基本相同。
6. GameObject.FindGameObjectsWithTag()
查找所有具有指定 Tag 的激活物体,返回数组。
GameObject[] enemies =
GameObject.FindGameObjectsWithTag("Enemy");
foreach (GameObject enemy in enemies)
{
Debug.Log(enemy.name);
}
特点:
- 返回
GameObject[]。 - 可以同时获取多个敌人。
- 不会包含非激活物体。
- 找不到匹配对象时返回空数组,而不是
null。
使用场景: 群体敌人处理、统计场景中激活的敌人数量等。
三、按组件类型查找物体
7. GetComponent<T>()
获取当前 GameObject 上指定类型的组件。
Rigidbody rb = GetComponent<Rigidbody>();
例如,角色物体上挂有 Rigidbody、BoxCollider、PlayerController,就可以分别获取这些组件。
PlayerController player = GetComponent<PlayerController>();
特点:
- 只查找当前 GameObject 上的组件。
- 找不到时返回
null。 - 返回组件本身,不是 GameObject。
- 一个物体有多个相同类型的组件时,通常使用
GetComponents<T>()获取全部。
使用场景: 获取刚体、碰撞体、Animator、自定义脚本等。
8. GetComponentInChildren<T>()
查找当前物体及其子物体上的指定组件。
Animator animator =
GetComponentInChildren<Animator>();
例如:
Player
└── Model
└── Animator
即使 Animator 挂在 Model 上,也可以从 Player 获取。
默认情况下,非激活的子物体不会被搜索;可以传入 true 将其纳入搜索:
Animator animator =
GetComponentInChildren<Animator>(true);
使用场景: 获取角色模型上的 Animator、子物体上的碰撞组件等。
9. GetComponentInParent<T>()
从当前物体开始,向父级查找指定组件。
PlayerController player =
GetComponentInParent<PlayerController>();
例如:
Player(挂有 PlayerController)
└── Weapon
└── Bullet
子弹碰到敌人时,如果需要找到所属玩家,可以从子弹向上查找玩家控制器。
特点:
- 从当前物体开始,逐级向父物体搜索。
- 找到第一个匹配组件后返回。
- 默认不会搜索不激活的父物体。
使用场景: 武器查找角色、子物体查找父级管理脚本、碰撞对象查找所属单位。
10. GetComponents<T>()
获取当前物体上所有指定类型的组件。
Collider[] colliders = GetComponents<Collider>();
例如,一个物体上同时挂有多个 Collider,可以一次性获取它们。
使用场景: 同一个物体上有多个碰撞体,需要统一设置属性或处理碰撞逻辑。
11. TryGetComponent<T>()
尝试获取指定组件,并通过 out 参数返回结果。
if (TryGetComponent<Rigidbody>(out var rb))
{
rb.AddForce(Vector3.up);
}
特点:
- 获取成功返回
true,失败返回false。 - 可以直接进行条件判断。
- 适合组件可能存在、也可能不存在的情况。
使用场景: 碰撞检测、通用交互、可选组件处理。
四、按场景中的组件类型查找
这类方法不是按名称查找,而是直接搜索场景中符合类型的对象。
12. FindFirstObjectByType<T>()
在已加载的对象中,查找第一个符合类型的活动对象。
GameManager manager =
FindFirstObjectByType<GameManager>();
例如,场景中有一个挂载了 GameManager 的物体,就可以获取其组件。
特点:
- 按组件类型查找,而不是按名称。
- 默认不包含非激活对象。
- 找不到时返回
null。 - 不要依赖多个匹配对象时的查找顺序。
使用场景: 启动时临时获取场景管理器、音频管理器等。
13. FindAnyObjectByType<T>()
查找任意一个符合类型的活动对象。
AudioManager audioManager =
FindAnyObjectByType<AudioManager>();
与 FindFirstObjectByType<T>() 相比,FindAnyObjectByType<T>() 不要求返回第一个对象,适合只需要获取一个匹配对象的情况。
使用场景: 场景中存在多个同类型对象,但只需要其中一个。
14. FindObjectsByType<T>()
查找场景中所有符合指定类型的对象,返回数组。
Unity 6 中可以这样使用:
Enemy[] enemies =
FindObjectsByType<Enemy>(FindObjectsSortMode.None);
如果需要把非激活物体也纳入搜索:
Enemy[] enemies =
FindObjectsByType<Enemy>(
FindObjectsInactive.Include,
FindObjectsSortMode.None);
特点:
- 返回所有匹配的组件。
FindObjectsSortMode.None表示不排序,通常能减少搜索开销。- 不会自动包含 Prefab 资源等未作为场景对象加载的资源。
Scripting API: Object.FindObjectsByType+1
使用场景: 查找所有敌人、统计场景中的特定管理组件、批量处理某类对象。
15. FindObjectOfType<T>() 和 FindObjectsOfType<T>()
这是很多旧教程中经常出现的 API。
// 旧写法:查找一个
GameManager manager = FindObjectOfType<GameManager>();
// 旧写法:查找全部
Enemy[] enemies = FindObjectsOfType<Enemy>();
在 Unity 6 中,建议使用:
FindFirstObjectByType<T>()FindAnyObjectByType<T>()FindObjectsByType<T>()
旧 API 已被标记为过时,写新代码时优先使用新的 API。
五、通过父子层级查找
16. transform.root
获取当前物体最顶层的父级 Transform。
Transform root = transform.root;
例如:
Player
└── Model
└── Weapon
└── Bullet
从 Bullet 上调用 transform.root,可以获取最顶层的 Player。
使用场景: 从深层子物体获取所属角色的根节点。
注意:它返回的是 Transform,如果需要 GameObject,可以使用 transform.root.gameObject。
17. transform.parent
获取当前物体的直接父物体。
Transform parent = transform.parent;
例如,从 Weapon 获取父级,会得到 Player。
如果当前物体没有父物体,则返回 null。
使用场景: 获取 UI 父节点、武器所属物体、物品所在容器。
18. 遍历子物体
当需要查找父物体下面的所有直接子物体时,可以通过 childCount 和 GetChild() 遍历。
for (int i = 0; i < transform.childCount; i++)
{
Transform child = transform.GetChild(i);
Debug.Log(child.name);
}
如果要递归查找所有层级,需要自己编写递归方法,或者通过其他遍历方式实现。
使用场景: 背包格子、UI 列表、地图节点、动态生成的子物体。
六、特殊查找方法
19. Resources.FindObjectsOfTypeAll<T>()
查找所有已加载的指定类型对象,搜索范围比常规场景查找更广。
GameObject[] objects =
Resources.FindObjectsOfTypeAll<GameObject>();
它可能包含场景对象、资源对象,以及编辑器中加载的其他对象,因此不能简单地将返回结果全部当成游戏场景中的活动物体。
使用场景: 编辑器工具开发、特殊调试、需要查找非激活对象或已加载资源的场景。
注意:它可能返回大量对象,通常不适合普通游戏逻辑中的频繁调用。
20. 通过 Inspector 序列化引用
这不是运行时搜索 API,但在实际开发中通常是更推荐的方式。
[SerializeField]
private Transform player;
[SerializeField]
private Animator animator;
在 Inspector 中将对应物体或组件拖入字段即可直接使用。
使用场景: 玩家控制器、UI 管理器、固定的场景引用、预制体内部的组件引用。
优点:
- 不需要运行时搜索。
- 引用关系直观。
- 可以减少因物体重命名或查找失败导致的问题。
七、通过物理检测查找附近物体
这类方法严格来说不是通用的物体查找 API,而是按照空间位置、碰撞体或射线检测目标。
21. Physics.Raycast()
从指定位置发射射线,检测是否命中物体。
if (Physics.Raycast(
transform.position,
transform.forward,
out RaycastHit hit,
10f))
{
Debug.Log(hit.collider.gameObject.name);
}
使用场景: FPS 射击、鼠标点击 3D 物体、角色视线检测、交互检测。
22. Physics.OverlapSphere()
检测指定球形范围内的所有碰撞体。
Collider[] hits =
Physics.OverlapSphere(transform.position, 5f);
例如,可以用来查找角色周围 5 米内的敌人。
使用场景: 范围攻击、爆炸伤害、附近敌人检测。
如果项目使用 2D 物理,则对应使用 Physics2D.Raycast()、Physics2D.OverlapCircleAll() 等方法。
八、常用方法对照表
| 方法 | 查找依据 | 返回结果 | 典型用途 |
|---|---|---|---|
GameObject.Find() | 名称或层级路径 | 一个 GameObject | 查找指定物体 |
Transform.Find() | 子层级名称或路径 | 一个 Transform | 查找子物体 |
GetChild() | 子物体索引 | 一个 Transform | 遍历子物体 |
FindWithTag() | Tag | 一个 GameObject | 查找玩家 |
FindGameObjectsWithTag() | Tag | GameObject 数组 | 查找所有敌人 |
GetComponent<T>() | 当前物体上的组件类型 | 一个组件 | 获取 Rigidbody |
GetComponentInChildren<T>() | 当前物体及子层级 | 一个组件 | 获取 Animator |
GetComponentInParent<T>() | 当前物体及父层级 | 一个组件 | 查找所属角色 |
TryGetComponent<T>() | 当前物体上的组件类型 | bool 和组件 | 安全获取可选组件 |
FindFirstObjectByType<T>() | 组件类型 | 一个组件 | 查找场景管理器 |
FindAnyObjectByType<T>() | 组件类型 | 一个组件 | 获取任意匹配对象 |
FindObjectsByType<T>() | 组件类型 | 组件数组 | 查找所有敌人 |
transform.root | 层级根节点 | 一个 Transform | 获取角色根节点 |
transform.parent | 直接父节点 | 一个 Transform | 获取父物体 |
Resources.FindObjectsOfTypeAll<T>() | 已加载对象类型 | 对象数组 | 编辑器工具、特殊调试 |
Physics.Raycast() | 射线与碰撞体 | 命中信息 | 射击、交互 |
Physics.OverlapSphere() | 球形空间范围 | Collider 数组 | 范围检测 |
九、实际开发应该优先使用哪个?
已知物体的引用关系
优先使用 Inspector 序列化引用,或者通过初始化方法传入引用。
查找自己的子物体
优先使用 Transform.Find()、GetChild() 或遍历层级。
根据分类查找对象
可以使用 Tag,或者自己维护敌人、玩家等对象的集合。
查找场景中的某类组件
使用 FindFirstObjectByType<T>() 或 FindObjectsByType<T>(),注意搜索成本。
查找附近或射线命中的目标
使用 Physics 或 Physics2D 的检测 API。
面试重点: 不要只记方法名称,还要能说清楚它们的搜索范围、返回类型、是否包含非激活对象,以及查找失败时的表现。尤其要记住:GameObject.Find() 是按名称查找物体,GetComponent<T>() 是获取组件,而 FindObjectsByType<T>() 是按组件类型搜索多个对象。