趁着周末,再来一发。对于Unity3D,我也是刚开始学习,希望能够与大家多多交流。好了,废话不多说,下面继续。
本篇文章使用C#进行举例和说明。关于Unity 3D编辑器中的各种窗口,网上有很多资料了,这里不做介绍,默认大家都知道各个窗口的作用了,并会在Unity 3D编辑器中执行基本的操作。
上一篇讲的是Unity 3D的基础知识,这篇讲一下我刚开始学习编写脚本时遇到的一些困惑,主要是Unity 3D自身继承结构方面的问题。
脚本作为Unity 3D中的一个组件,是经常会使用到的。一个对象可以添加脚本组件,添加方式有两种:一种是直接拖到对象上,另一种是在对象的Inspector窗口中点击最下面的Add Component,然后选择需要的脚本就可以添加成功。第二种方式如下图所示:
我们在编写脚本时,经常需要用到对各种场景对象(GameObject)的访问,在刚开始的使用时,对于对象的访问方法一直都比较模糊。很多教程上都是这么写的:定义一个类型为Transform的变量,然后将该变量与一个游戏对象关联,然后使用。
关联变量与游戏对象主要有两种方式。
方式一:当脚本添加到一个游戏对象上时,在该游戏对象的Inspector窗口可以看到该脚本里面定义的public变量(见下图红框中内容)。那么将需要关联游戏对象的Transform变量的访问属性指定为public,然后将游戏对象从Hierarchy直接拖到这个变量上面就可以了。游戏音乐等其他资源也都可以通过这种方式关联,不过游戏音乐一般会放在Project窗口中,将其从Project窗口直接拖动到AudioClip类型变量上即可,当然这个变量也得是public,否则只能通过代码的方式获取。
方式二:在代码中使用类似GameObject.Find函数,与Find相关的有许多函数,例如FindGameObjectsWithTag()之类的,这些函数大家都可以去网上查询,Unity 3D官方文档里也有。各种Find函数都可以用来获取游戏对象。
刚开始学习时,我的疑惑是:明明是游戏对象时GameObject类型,而Transform只是一个组件,为什么很多地方使用Transform去代表这个对象,然后通过Transform对象对其进行一些操作?
为了解决我这个疑问,我去查了Unity 3D类结构图。它的设计刚开始看起来还有点绕,看明白的才知道是怎么回事。下面就讲一下我对这部分的理解。
在Unity 3D中,不管是游戏对象(GameObject)类还是Component(组件)类,都继承于Object类。这里要明确:虽然游戏对象中包含了各种组件,但是它们从继承结构上来说是平级的。
其中GameObject对象中包含了例如:audio, collider, rigidbody等组件变量,这些变量所关联的组件都是依附于该GameObject对象上的,同时也可以使用GetComponent获取依附于该对象上的组件(这两种方式都可以获取该对象上的组件),即下面两种方式效果是一样的(C#):
1 GameObject obj;
2 Rigidbody rigid = obj.GetComponent<rigidbody>();
3 Rigidbody rigid = obj.rigidbody;
第一种组件获取方式比较方便,而第二种组件获取方式则比较通用。例如要获取一个脚本组件,GameObject默认是没有该变量的,那么此时只能通过第二种方式获取。例如脚本类名是Gun,那么在该脚本依附的GameObject上获取方式就是: Gun gun = obj.GetComponent<Gun>(); 如果该GameObject没有该组件,则返回null。
在Component类中,也包含了例如:audio, collider, rigidbody等组件变量,这些变量表示该Component所依附的对象上的audio组件、collider组件、rigidbody组件等。audio、collider、rigidbody等这些变量的类型分别是Audio类、Collider类和Rigidbody类,这些具体的组件类又都是继承于Component类的。即Component类中包含了继承于它的具体组件的对象,这些对象关联到了该组件依附于GameObject上的组件。而且,Component类中也有一个GetComponent方法,这个函数与GameObject中的功能是一样的,能够通过该函数获取该组件依附于的GameObject上的其他组件。
举个例子,有一个GameObject obj,其中包含了两个组件:Transform和Rigidbody。
1 public GameObject obj; // 可以通过“关联变量与游戏对象的方式一”与对象进行关联,将其关联到一个包含Transform组件和Rigidbody组件的对象上
2 Transform m_trans1 = obj.transform;
3 Transform m_trans2 = obj.getComponent<Transform>();
4 Rigidbody m_rigid1 = obj.rigidbody;
5 Rigidbody m_rigid2 = obj.getComponent<rigidbody>();
那么m_trans1、m_trans2、m_trans1.transform、m_rigid1.transform和m_rigid1.getComponent<Transform>()等都指向同一个东西,就是obj的Transform组件。同理m_rigid1、m_rigid2、m_trans1.rigidbody和m_rigid1.rigidbody等也是都代表了obj的Rigidbody组件,这几种访问方式都是可以的。
这也就解释了我的疑惑,由于每个GameObject都必须有Transform组件,通过Transform组件,我们可以访问到该GameObject的其他组件,因此可以用Transform去代表这个GameObject。如果我们用Rigidbody去代表该GameObject就不好了,因为不是每个GameObject都有Rigidbody组件。Unity 3D提供的这组结构,使得组件的访问方法不止一种,这也是给初学者最大困惑的地方,不过我现在还不知道这几种访问方法在性能上是不是有区别(哪位小伙伴知道的话可以分享分享),但是不考虑性能的话,这几种访问方法都是可以的。
看了上述那么复杂的结构,是不是感觉比较诡异?尤其是上面代码中提到的m_trans1和m_trans1.transform都指向同一个东西,刚开始学的时候看着还是很晕的。同时,在这里要感谢一下这位仁兄,看了他这篇文章《Unity 3D编程概览》,使我对于Unity 3D的类结构有了更清晰的认识。
上面就是我对Unity 3D类结构的学习心得,由于刚开始学习,理解还不够深,如果文中存在不对的地方欢迎指正。