前言

  1. 无论是哪种方法,为了性能的考虑,请不要将获取物体的代码写在Update里

  2. 请注意GameObject和gameObject的区别

  3. 请注意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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void ObjectFind()
{
// 找父级
GameObject parent = GameObject.Find("GameObject");
Debug.Log("找父级物体,是否找到:" + (parent != null));

// 找子级
GameObject child = GameObject.Find("Child");
Debug.Log("找子级物体,是否找到:" + (child != null));

// 找父级隐藏物体
GameObject parentHide = GameObject.Find("GameObjectHide");
Debug.Log("找父级隐藏物体,是否找到:" + (parentHide != null));

// 找子级隐藏物体
GameObject childHide = GameObject.Find("ChildHide");
Debug.Log("找子级隐藏物体,是否找到:" + (childHide != null));
}

测试结果如下图:

img

当有使用GameObject.Find("GameObject"), 场景中有多个名为“GameObject”的物体存在时,将每个“GameObject”设置为不同的标签,多运行几次查看结果。

测试场景如下:

img

实例

测试代码如下:

1
2
3
// 找同名物体
GameObject nameObj = GameObject.Find("GameObject");
Debug.Log("找同名,是否找到:" + nameObj.tag);

测试结果: 查找顺序是:“自身”(挂载脚本的物体) –> 和自身同层级上面物体 –> 和自身同层级下面物体 –> 自身子物体 –> 自身父物体。

img

Object.Find()得出结论:

  1. 全局查找参数名称游戏物体;
  2. 不对禁用(隐藏)物体进行查找;
  3. 若有同名物体时根据层级关系进行查找。

使用建议: 有同名物体存在时,尽量不要使用Object.Find()进行查找,或者说使用Object.Find()进行查找时,应控制查找物体命名唯一。

FindGameObjectWithTag()

GameObject.FindGameObjectWithTag() 根据标签查找游戏物体并返回。
GameObject.FindGameObjectsWithTag() 根据标签查找当前场景中所有这个标签的游戏物体并返回所有物体的数组。

将如下场景:除主摄像机(Main Camera)外的所有游戏物体的标签(Tag)都修改为Player,进行测试。

img

实例

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
void GameObjectFindWithTag()
{
GameObject tagObj = GameObject.FindGameObjectWithTag("MainCamera");
Debug.Log("根据标签查找游戏物体,是否查到:" + (tagObj != null));

GameObject[] tagObjs = GameObject.FindGameObjectsWithTag("Player");
for (int i = 0; i < tagObjs.Length; i++)
{
Debug.Log("根据标签查找游戏物体名称:" + tagObjs[i].name);
}
}

测试结果:

img

实例

查找不存在的标签测试:

1
2
GameObject tagObj = GameObject.FindGameObjectWithTag("MainCamera1");
Debug.Log("根据标签查找游戏物体,是否查到:" + (tagObj != null));

img

报错:UnityException: Tag: MainCamera1 is not defined. 翻译: MainCamera1是一个未定义的标签

FindGameObjectWithTag()得出结论:

  • 查找不到禁用物体,使用时需确认要查找的物体是启用(显示)状态;
  • 有多个有游戏物体使用同一标签时,尽量不使用FindGameObjectWithTag此方式查找单一游戏体,因为查找顺序会受到层级影响;
  • 查找未定义标签会报错,使用时需确认查找的字符串是已定义的标签;
  • 查找的标签是已定义但是未使用过,会找不到游戏物体,返回空值。

GameObject.FindObjectOfType()

和上面根据标签查找的逻辑差不多。

GameObject.FindObjectOfType<类型>(); :根据类型(组件/自定义脚本)查找并返回这个类。

GameObject.FindObjectsOfType<类型>() :根据类型(组件/自定义脚本)查找当前场景中所有这个类并返回一个这个类的数组。

实例

1
2
3
4
5
6
7
8
9
10
11
void FindObjectOfType()
{
Camera typeCamera = GameObject.FindObjectOfType<Camera>();
Debug.Log("根据类型查找物体,是否查到:" + (typeCamera != null));

Transform[] typeTransArr = GameObject.FindObjectsOfType<Transform>();
for (int i = 0; i < typeTransArr.Length; i++)
{
Debug.Log("根据类型查找到的物体名称:" + typeTransArr[i].name);
}
}

img

FindObjectOfType()得出结论:

  • 查找不到禁用物体,使用时需确认要查找的物体是启用(显示)状态;
  • 查找场景中不存在类型时会返回null,不会报错;

通常使用情况为:初始化时在一个脚本中获取另一个脚本的引用,通过这种形式查找。【后多被单例取代】

Transform.Find()

实例

查找挂载物体父级,同级,子级物体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void TransformFind()
{
// 找父级
Transform parent = transform.Find("Root");
Debug.Log("找父级物体,是否找到:" + (parent != null));

// 找同级
Transform selfObj = transform.Find("Parent_1");
Debug.Log("找同级物体,是否找到:" + (selfObj != null));

// 找子级
Transform child = transform.Find("Child");
Debug.Log("找子级物体,是否找到:" + (child != null));

// 找子级隐藏物体
Transform childHide = transform.Find("ChildHide");
Debug.Log("找子级隐藏物体,是否找到:" + (childHide != null));
}

img

实例

找多层级子物体:

1
2
3
4
5
6
// 找二级子物体
Transform child_1 = transform.Find("Child_1_1");
Debug.Log("找二级子物体 参数只写名称,是否找到:" + (child_1 != null));
// 找二级子物体
Transform child_1_1 = transform.Find("Child/Child_1_1");
Debug.Log("找二级子物体 参数写全路径,是否找到:" + (child_1_1 != null));

img

Find()得出结论:

  • 只能找其子物体,不能找其同级或更高层级物体
  • 找子物体时不考虑是否被禁用(隐藏)
  • 找多层子物体时需写全路径(否则即使存在也找不到)

Transform.FindObjectOfType()

经过测试和GameObject.FindObjectOfType()没什么区别,测试结果一致,测试代码和截图就不发处理占地方了。

测试时我发现 GameObject.FindObjectsOfType<类型>()Transform.FindObjectsOfType() 被合并了,应该说完全是一个方法了,根据下图可以看到,我虽然前打的是Transform的标签,但是它是灰色的,鼠标放上去看到方法引用的却是GameObject.FindObjectsOfType

img

结论

得出结论:
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
2
3
4
for (int i = 0; i < transform.childCount; i++)
{
Debug.Log(transform.transform);
}

获取当前物体的父物体transform.parent

获取当前物体的根物体transform.root

transform.GetChild()使用总结:

  • 以自身为基础,查找子物体(注意索引从0开始,写多报错)
  • 可以使用transform.parent.parent 的形式无限向上,然后再GetChild(),就达到了查找父层级或更高层级物体的目的
  • 弊端是依赖游戏物体的层级关系,使用时需确保层级关系相对稳定。若不稳定会导致每次修改游戏体时还要修改代码,这就加大了工作量了。
- [Unity查找游戏物体的六种方式详解](https://www.zhangshengrong.com/p/9Oab8pQ5Xd/) - 表 [最清晰的Unity查找物体的几种方法及优缺点详解!其他教程有很多没注意的地方,请看这里!](https://blog.csdn.net/heliocentricism/article/details/109062975) -