Unity查找物体的6种方法详解
前言
无论是哪种方法,为了性能的考虑,请不要将获取物体的代码写在Update里
请注意GameObject和gameObject的区别
请注意Transform和transform的区别
查找物体方法(速查表)
GameObject(Unity的所有物体的基类,针对全局,方法是静态方法,所以可以直接用)
注意:非gameObject,注意大小写,gameObject指的是该物体,gameObject无这些方法transform(是本物体的transform的组件,所以查找都是针对本物体的)
注意:非Transform,注意大小写,Transform指的是Transform类型,Transform无这些方法P.S.
transform
效率比GameObject
高,强烈建议使用transform
查找物体的6种方法 | 方法 | 优点 | 缺点 |
---|---|---|---|
GameObject.Find (物体名字) |
递归遍历全局物体,直到找到第一个为Capsule的物体 | 全局搜索 | 1. 无法查找隐藏对象 2.效率低下(相当于递归遍历查找) |
GameObject.Find (“GameObject/ChildGameObject) |
通过路径查找 | 1:解决查找中可能出现的重名问题。 2:如果有完全的路径,减少查找范围,减少查找时间 |
1.路径或结构调整后,需要重新调整路径字段。 2.如果路径查找中的任何一个父节点被隐藏(Active=false),这个对象都将查找不到。 |
GameObject.FindWithTag (Tag字段) GameObject.FindGameObjectsWithTag (Tag字段) |
全局找到 (第一个)/(所有) Tag为Capsule的物体 | tag的搜索速度比Find来的快 | |
transform.Find(物体名字) | 找到该脚本挂的物体的 第一个 为Capsule的孩子(本质为路径查找) | 1.可以查找隐藏对象 2.支持路径查找 3.查找隐藏对象的前提是transform所在的根节点必须可见(active=true) |
1.局部查找 2.不能直接找到平级的物体及他们的孩子 |
transform.GetChild(Index) | 找到该脚本挂的物体的 第一个孩子 | 效率高 | 1.局部查找 |
Resources.FindObjectsOfTypeAll () | 返回指定类型的对象列表(如XXXScript)。主要用于编辑器中,eg。检测内存泄露、批量查找的功能等 | 用得少,几乎不用 | 用得少,几乎不用 |
Object.Find()
Object.Find()
:根据名称找到游戏对象并返回它。
实例
1 | void ObjectFind() |
测试结果如下图:
当有使用GameObject.Find("GameObject")
, 场景中有多个名为“GameObject”的物体存在时,将每个“GameObject”设置为不同的标签,多运行几次查看结果。
测试场景如下:
实例
测试代码如下:
1 | // 找同名物体 |
测试结果: 查找顺序是:“自身”(挂载脚本的物体) –> 和自身同层级上面物体 –> 和自身同层级下面物体 –> 自身子物体 –> 自身父物体。
Object.Find()得出结论:
- 全局查找参数名称游戏物体;
- 不对禁用(隐藏)物体进行查找;
- 若有同名物体时根据层级关系进行查找。
使用建议: 有同名物体存在时,尽量不要使用Object.Find()
进行查找,或者说使用Object.Find()
进行查找时,应控制查找物体命名唯一。
FindGameObjectWithTag()
GameObject.FindGameObjectWithTag()
根据标签查找游戏物体并返回。
GameObject.FindGameObjectsWithTag()
根据标签查找当前场景中所有这个标签的游戏物体并返回所有物体的数组。
将如下场景:除主摄像机(Main Camera)外的所有游戏物体的标签(Tag)都修改为Player,进行测试。
实例
测试代码如下:
1 | void GameObjectFindWithTag() |
测试结果:
实例
查找不存在的标签测试:
1 | GameObject tagObj = GameObject.FindGameObjectWithTag("MainCamera1"); |
报错:UnityException: Tag: MainCamera1 is not defined.
翻译: MainCamera1是一个未定义的标签
FindGameObjectWithTag()得出结论:
- 查找不到禁用物体,使用时需确认要查找的物体是启用(显示)状态;
- 有多个有游戏物体使用同一标签时,尽量不使用FindGameObjectWithTag此方式查找单一游戏体,因为查找顺序会受到层级影响;
- 查找未定义标签会报错,使用时需确认查找的字符串是已定义的标签;
- 查找的标签是已定义但是未使用过,会找不到游戏物体,返回空值。
GameObject.FindObjectOfType()
和上面根据标签查找的逻辑差不多。
GameObject.FindObjectOfType<类型>();
:根据类型(组件/自定义脚本)查找并返回这个类。
GameObject.FindObjectsOfType<类型>()
:根据类型(组件/自定义脚本)查找当前场景中所有这个类并返回一个这个类的数组。
实例
1 | void FindObjectOfType() |
FindObjectOfType()得出结论:
- 查找不到禁用物体,使用时需确认要查找的物体是启用(显示)状态;
- 查找场景中不存在类型时会返回null,不会报错;
通常使用情况为:初始化时在一个脚本中获取另一个脚本的引用,通过这种形式查找。【后多被单例取代】
Transform.Find()
实例
查找挂载物体父级,同级,子级物体:
1 | void TransformFind() |
实例
找多层级子物体:
1 | // 找二级子物体 |
Find()得出结论:
- 只能找其子物体,不能找其同级或更高层级物体
- 找子物体时不考虑是否被禁用(隐藏)
- 找多层子物体时需写全路径(否则即使存在也找不到)
Transform.FindObjectOfType()
经过测试和GameObject.FindObjectOfType()
没什么区别,测试结果一致,测试代码和截图就不发处理占地方了。
测试时我发现 GameObject.FindObjectsOfType<类型>()
和Transform.FindObjectsOfType()
被合并了,应该说完全是一个方法了,根据下图可以看到,我虽然前打的是Transform
的标签,但是它是灰色的,鼠标放上去看到方法引用的却是GameObject.FindObjectsOfType
。
结论
得出结论:
Transform.FindObjectOfType() 和 GameObject.FindObjectOfType()使用方式一样,结果也没有区别…
transform.GetChild()
Transform.GetChild()
是找子物体的方法,也是我个人比较喜欢用的方式,弊端是不能随意修改游戏物体的层级关系。
使用起来也很简单
实例
比如:找一级子物体的第一个物体
1 | Transform child1 = transform.GetChild(0); |
找一级子物体的第一个物体的第三个子物体
1 | Transform child1 = transform.GetChild(0).GetChild(2); |
使用方式:几个层级就几个GetChild()
,参数就是当前层级的第几个物体(从0开始)
使用拓展:
遍历子物体:
1 | for (int i = 0; i < transform.childCount; i++) |
获取当前物体的父物体transform.parent
获取当前物体的根物体transform.root
transform.GetChild()使用总结:
- 以自身为基础,查找子物体(注意索引从0开始,写多报错)
- 可以使用transform.parent.parent 的形式无限向上,然后再GetChild(),就达到了查找父层级或更高层级物体的目的
- 弊端是依赖游戏物体的层级关系,使用时需确保层级关系相对稳定。若不稳定会导致每次修改游戏体时还要修改代码,这就加大了工作量了。