首页 > Unity3D频道 > 【你好Unity3D】 > #你好Unity3D#Unity获取游戏对象详解(来自我的长微博)
2014
06-16

#你好Unity3D#Unity获取游戏对象详解(来自我的长微博)

我觉得Unity里面的Transform 和 GameObject就像两个双胞胎兄弟一样,这俩哥们很要好,我能直接找到你,你也能直接找到我。我看很多人喜欢在类里面去保存GameObject对象。解决GameObject.Find()无法获取天生activie = false的问题。
     private GameObject root ;

我觉得你最好不要保存GameObject ,而是去保存Transform ,因为Transform下的方法要比GameObject多,使用频率也要高很多。

    private Transform root ;

其实我心里一直有个疑问,为什么unity不把GameObject和Transform合并成一个对象。

1.GameObject.Find()

通过场景里面的名子或者一个路径直接获取游戏对象。
    GameObject root = GameObject.Find(“GameObject”);

我觉得如果游戏对象没再最上层,那么最好使用路径的方法,因为有可能你的游戏对象会有重名的情况,路径用“/”符号隔开即可。
    GameObject root = GameObject.Find(“GameObject/Cube”);

GameObject.Find()使用起来很方便,但是它有个缺陷如下图所示,就是如果你的这个GameObject天生acive = false的话。那么你用GameObject.Find()是永远也无法获取它的对象的。如果对象都获取不到,那么对象身上脚本啊 组件啊啥的都是获取不到的,变成了没有意义的对象。

#你好Unity3D#Unity获取游戏对象详解(来自我的长微博) - 雨松MOMO程序研究院 - 1 

就这个问题我查过很多资料,最终也无果。。但是我用另外一个巧妙的办法可以解决它。(后面详解)或者你也可以提前把所有的游戏对象保存在内存中。

GameObject.Find()方法在游戏中的使用频率很高。但是它也很消耗性能,你可以想想它的原理肯定也是用类似递归的形式来做的,那么我们就要尽量更少的调用GameObject.Find()方法,可以把获取的游戏对象,保存在内存里,这是再好不过的选择了。 尤其是在Update方法中不要去 Find()游戏对象!!

2 .Transform.Find()
还记得上面我说过用GameObject无法获取天生acive = false的游戏对象,如果你用Transform.Find()的话就可以很好的获取,另外Unity还提供了一个Transform.FindChind()的方法,这个方法未来会被unity废弃,大家最好就别用了,用Transform.Find()可以取代。

如下代码,我们先获取顶级对象root 。接着用Find()去找它的子节点”xxxx”的对象,无论”xxxx”对象是否active = true 都是可以直接找到对象的。

       

 

Find()方法只能直接去找子节点,如果你想找 孙节点,那么可以用”/“符号把层级关系隔开,找起来很方便。同样无论”xxxx”对象是否active = true 都是可以直接找到对象的。

值得注意的是,unity规定了比如父节点active = true 并且子节点的 active = true 都满足的情况下 才能全部显示。使用Transform.Find()可以很方便的获取游戏对象,因为有了游戏对象,那么它身上的脚本啊组件啊什么的都可以很方便的获取到。

但是Transform.Find()必须要保证你的顶级父对象的activity = true。举个例子,你做了一个场景有一些地图你在场景里面预先activie = false了, 你希望在游戏中的某个时间点把它们都打开 setActive(true)

你可以把“map”节点放在一个active = true的GameObject上,无论是关闭 或者 显示 代码中写起来都很方便。 假如你的map节点就是顶级节点,那么它一旦天生acive = false ,那么你将无法得到它的对象,更无法设置它的属性了。

 GameObject root = GameObject.Find(“GameObject”);        

GameObject map =  root.transform.Find(“map”).gameObject;       

 map.SetActive(true);

#你好Unity3D#Unity获取游戏对象详解(来自我的长微博) - 雨松MOMO程序研究院 - 2

3. unity 还提供了几个获取游戏对象的方法,但是我个人觉得使用频率不高,这里也简单说两句。

GameObject.FindGameObjectsWithTag(“tag”)
GameObject.FindWithTag(“tag”)

根据一个标记来获取游戏对象,返回一个 或者 一个数组,我个人觉得这个两个方法没啥用,因为既然需要用到标记那么相比这个游戏对象必然是非常特殊的一个,所以我会把它存在内存中。

Object.FindObjectOfType
Object.FindObjectsOfType
Resources.FindObjectsOfTypeAll 

根据一个类型返回Object,比如 GameObject 、Texture、Animation 、甚至还可以是你自己写的一个脚本 的范型。它找起来很方便,可以返回一个 或者一个数组。 我觉得这几个方法其实游戏中也没啥用,不过在编辑器中使用的确实很频繁,比如你要做批量检查场景的工具,查找场景中有没有使用某个特殊类型的对象。 或者查看内存的占用量,看看当前内存中那些Texture没有被释放掉。 等等。

还有一个方法,如果你知道自对象的索引,还可以用下面的方法来获取,参数是index的索引。
transform.GetChild(0)

找到了一个即使隐藏root节点gameObject也能进行查找的方法。http://answers.unity3d.com/questions/52560/gameobjectfind-work-on-inactive-objects.html

 

雨松MOMO提醒您:亲,如果您觉得本文不错,快快将这篇文章分享出去吧 。另外请点击网站顶部彩色广告或者捐赠支持本站发展,谢谢!

--

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠如果您愿意花10块钱请我喝一杯咖啡的话,请用手机扫描二维码即可通过支付宝直接向我捐款哦。

  1. 差点忽悠了我,transform.Find跟该物体是否active一点关系没有;你把Find语句脚本放在该物体执行,这物体inactive,脚本都不执行,当然找不到

  2. 如果说我写一个插件需要查找场景里面的所有物体,那怎么查找里面隐藏的物体或者子物体。

  3. 大神,递归的话是从头到尾查遍一个对象及其子对象之后再查下一个,这样Find函数后面即是加了路径速度仍然会很慢。那unity会不会是用的从外层到内层的遍历方法呢?

  4. 大神,问一下有没有碰到过这种问题:一个新生成的Item(prefab),我在它上面用this.transform.Find()取它的子元素,居然取不到,这是为啥子?我保证是生成后,立马去取的,不是之前。后来用public,然后把子元素一个一个拖上去解决了,关键是这个好奇怪啊,取自己的GetComponent组件倒是能取到的。

  5. 看上面的意思GameObject.Find和Transform.Find找游戏对象性能都不是很好.那就要自己维护一份游戏对象了,这样也行,但是又要考虑释放的问题.

  6. 谈点个人看法:用GameObject.Find有几个个弊端,一个是不能防止重名,所以在Hierarchy内对GO的名字定义要慎重且不要随意修改,因为如果在Hierarchy列表中改变GO名字,如果很多地方Find了这个GO就会出问题,因为编译器不会报错,而实际上是找不到了。二是Momo说的active的问题。个人的处理是这样的,把某个对象集内所有GO都封装到专门的GO读取类,由这个类维护GO的name和GO对象列表。用递归函数遍历指定根节点下的所有子节点GO,需要调用的就加入到类对象列表。并提供单例接口来对该类进行访问。虽然写起来麻烦,但是修改调试还是方便点。PS:谢谢分享Transform.Find可以查未激活的组件这个方法。但是GameObject和Transform在逻辑上还是有区别的,个人认为不合并还是好些。

  7. 感觉Object.FindObjectOfTag用的还是挺多的,比如需要判断碰到的物体是否是敌人,而敌人有很多种的话,通过Find()来一个一个找敌人的名字还是挺麻烦的,不如直接找Enemy标签来的方便