101 lines
6.6 KiB
Markdown
101 lines
6.6 KiB
Markdown
### **Unity 场景物体查找API核心笔记**
|
||
|
||
在Unity中,动态查找场景中的游戏对象(GameObject)或组件(Component)是一项基本操作。不同的API适用于不同的场景,理解它们的区别和性能开销至关重要。
|
||
|
||
#### **一、 全局场景查找 (Scene-Wide Search)**
|
||
|
||
这类API会遍历整个场景的层级树来寻找匹配的对象。**它们的共同点是性能开销较大,绝对禁止在 `Update()` 或 `FixedUpdate()` 等高频函数中每帧调用。** 最适合在 `Awake()` 或 `Start()` 中用于初始化引用。
|
||
|
||
1. **按名称查找:`GameObject.Find()`**
|
||
|
||
- **API:** `public static GameObject Find(string name);`
|
||
|
||
- **作用:** 根据物体的**确切名称**查找一个**激活的(active)**游戏对象。如果存在多个同名对象,它会返回哪一个是不确定的。
|
||
|
||
- **返回:** `GameObject`。如果找不到则返回 `null`。
|
||
|
||
- **笔记:** 这是最不推荐的查找方式之一。因为它依赖于硬编码的字符串,当场景中物体改名时代码就会失效。性能非常差,应尽量避免使用。
|
||
|
||
2. **按标签查找:`FindWithTag()` / `FindGameObjectsWithTag()`**
|
||
|
||
- **API (单个):** `public static GameObject FindWithTag(string tag);`
|
||
|
||
- **API (多个):** `public static GameObject[] FindGameObjectsWithTag(string tag);`
|
||
|
||
- **作用:** 根据标签(Tag)查找一个或所有激活的游戏对象。标签需要在Inspector中预先为GameObject设置。
|
||
|
||
- **返回:** 单个返回 `GameObject`,多个返回 `GameObject[]` 数组。
|
||
|
||
- **笔记:** 比按名字查找要好,因为它不依赖于具体名称,更灵活。但本质上仍然需要遍历场景,性能开销依然很大。
|
||
|
||
3. **按组件类型查找:`FindObjectOfType()` / `FindObjectsOfType()` (常用)**
|
||
|
||
- **API (单个):** `public static T FindObjectOfType<T>() where T : Object;`
|
||
|
||
- **API (多个):** `public static T[] FindObjectsOfType<T>() where T : Object;`
|
||
|
||
- **作用:** 查找场景中挂载了特定组件 `T` 的一个或所有激活的对象。`T` 可以是任何继承自 `Component` 的脚本或Unity内置组件(如 `Camera`, `Light`)。
|
||
|
||
- **返回:** 单个返回组件 `T` 的引用,多个返回 `T[]` 数组。
|
||
|
||
- **笔记:** 这是最常用和推荐的**全局查找**方法。它不依赖名称或标签,而是直接关联代码逻辑(组件类型),更加健壮。但同样,性能开销大,**仅限初始化时使用**。
|
||
|
||
- **现代化替代方案:** Unity 推荐使用 `FindAnyObjectByType<T>()` 和 `FindObjectsByType<T>(...)` 作为更新、性能更好的替代品。
|
||
|
||
|
||
#### **二、 局部层级查找 (Hierarchy Search)**
|
||
|
||
这类API只在当前GameObject的子级或父级中进行查找,范围小,**性能远高于全局查找**。
|
||
|
||
1. **在自身上查找组件:`GetComponent<T>()`**
|
||
|
||
- **API:** `public T GetComponent<T>();`
|
||
|
||
- **作用:** 获取挂载在**同一个**游戏对象上的组件 `T`。
|
||
|
||
- **笔记:** 这是最常用、最高效的获取自身组件的方法。
|
||
|
||
2. **在子级中查找:`transform.Find()` / `GetComponentInChildren<T>()`**
|
||
|
||
- **API (按名找子物体):** `public Transform transform.Find(string name);`
|
||
|
||
- **API (按组件找子物体):** `public T GetComponentInChildren<T>();`
|
||
|
||
- **作用:**
|
||
|
||
- `transform.Find()`: 根据名称查找一个**直接子级**的 `Transform`。注意,它不会递归查找孙子级。
|
||
|
||
- `GetComponentInChildren<T>()`: 查找自身或其**所有子级**(包括孙子级等)中第一个挂载了组件 `T` 的对象。
|
||
|
||
- **笔记:** `GetComponentInChildren` 非常适合用来获取预制体(Prefab)内部的某个部件,例如获取枪械模型上的“枪口特效”组件。
|
||
|
||
3. **在父级中查找:`GetComponentInParent<T>()`**
|
||
|
||
- **API:** `public T GetComponentInParent<T>();`
|
||
|
||
- **作用:** 查找自身或其**所有父级**中第一个挂载了组件 `T` 的对象。
|
||
|
||
- **笔记:** 常用于UI或模块化设计。例如,一个按钮可以向上查找到它所属的那个“根面板”控制器脚本。
|
||
|
||
|
||
---
|
||
|
||
### **三、 性能与最佳实践总结 (至关重要)**
|
||
|
||
| 方法 | 查找范围 | 性能 | 推荐用法 |
|
||
| ------------------------ | -------- | ----------- | ------------------------------------------------------------------- |
|
||
| **公开变量引用** | 无 (手动指定) | **极高 (最佳)** | **首选方案!** 在脚本中声明 `public GameObject myObject;`,然后在Inspector中手动拖拽赋值。 |
|
||
| `GetComponent<T>` | 自身 | 非常高 | 在 `Awake`/`Start` 中获取自身其他组件。 |
|
||
| `GetComponentInChildren` | 自身及所有子级 | 较高 | 初始化时获取Prefab内部的固定部件。 |
|
||
| `GetComponentInParent` | 自身及所有父级 | 较高 | 模块化组件向上查找控制器或根对象。 |
|
||
| `FindObjectsOfType<T>` | 整个场景 | **很低** | **仅限**在管理器类的 `Awake` 中,用于查找并注册场景中所有特定类型的对象。 |
|
||
| `GameObject.Find()` | 整个场景 | **极低 (最差)** | **强烈不推荐**,仅用于快速原型或调试,正式项目中应被替换。 |
|
||
#### **核心原则笔记:**
|
||
|
||
1. **首选“拖拽引用”:** 在脚本中声明一个 `public` 或 `[SerializeField] private` 变量,然后在Unity编辑器里手动将场景中的物体拖拽到该变量上。这是**零开销**、最安全、最高效的方式。
|
||
|
||
2. **“Find”仅用于初始化:** 所有全局查找 (`Find`, `FindObjectOfType` 等) 都应该只在 `Awake()` 或 `Start()` 函数中调用**一次**,并将结果缓存到一个私有变量中,供后续使用。
|
||
|
||
3. **杜绝在Update中使用Find:** **永远不要**在 `Update()`, `FixedUpdate()`, `LateUpdate()` 中直接调用任何全局查找API。这是导致游戏卡顿的常见原因之一。
|
||
|
||
4. **善用局部查找:** 当物体关系固定时(如Prefab内部),优先使用 `GetComponentInChildren` 或 `transform.Find`,它们的性能远好于全局查找。 |