深入了解GameObject
创建GameObject
前面说到,GameObject必须存在于一个场景中。在场景中创造游戏对象的方法有:
在Hierarchy面板中使用Create命令创建新物体。
将导入工程的模型文件、图片文件和音乐文件从Project面板直接拖入场景创建对应的物体。
将Prefabs拖进场景创建物体。
在脚本中获取gameObject
可以在脚本类中使用gameObject属性来访问该组件所附加在的GameObject实例。
新建一个名为PrintGameObject的cs脚本,将PrintGameObject类的内容仿照如下代码进行修改,然后将这个脚本附加在任意一个GameObject上,运行试玩,可以观察到Console面板输出的内容中包含这个脚本所附加的GameObject的名字。
class PrintGameObject : MonoBehavior
{
void Start()
{
Debug.LogFormat("Hello {0}!",gameObject);
}
}
GameObject的属性
选中一个GameObject后,可以在Inspector面板中访问GameObject的属性:
图标
点击左侧的白色图标可以更改GameObject的显示图标,这个图标只在编译器中生效,不会被打包。
Active
图标选项右侧的勾选框,对应gameObject对象的activeSelf属性,控制物体的渲染和逻辑运作。当activeSelf == false时,该GameObject及其所有子物体都不会被渲染,都不再计算物理和碰撞,都不再运行附加其上的脚本,不再运行协程中的代码,但会继续运行Invoke中的代码。
在脚本中activeSelf属性是只读的,我们可以使用SetActive函数来改变这个属性:
public bool GameObject.activeSelf{get; private set;}
public void GameObject.SetActive(bool active);
class PrintGameObject : MonoBehavior
{
void Start()
{
if(gameObject.activeSelf)
{
gameObject.SetActive(flase);
Debug.LogFormat("The active is {0}.", gameObject, gameObject.activeSelf);
}
}
}
Unity处理activeSelf的逻辑是:每帧开始前检测物体的activeSelf属性,如果为false则跳过这次帧周期的逻辑运算。所以,在代码中使用SetActive将该属性改为false并不会导致代码运行中断,而会等到下一帧开始时才生效。因此运行上面的脚本时,LogFormat并不会因为activeSelf == false而不被运行。
Name
Active右侧的输入框,对应GameObject的name属性。使用Debug.Log输出GameObject时,实际输出的就是GameObject.name。name的用处除了在Hierarchy中命名方便管理外,还可以用于GameObject的查找,在场景中新建一个空物体改名为"Empty",再新建一个空物体改名为"Manager",将下方的脚本附加到Manager上,观察结果。
string GameObject.name{get;set;}
GameObject GameObject.Find(string name);
class PrintGameObject : MonoBehavior
{
void Start()
{
GameObjcet empty = GameObjcet.Find("Empty");
if(empty == null)
{
Debug.LogError("We can't find any GameObjcet named \"Empty\"! ");
}
else
{
empty.name = "NewName";
Debug.Log("The name of what we find has changed to {0}!", empty);
}
}
}
众所周知,判别String的算法的时间复杂度为O(n),Find方法将遍历整个Hierarchy并消耗大量的资源,所以尽量使用直接传值的方式获取需要的GameObject。
Static
Name右侧的选项组。该选项组选择为Everything时选框显示为钩号,当选择了部分选项时显示为减号,当没有选择任何选项时选框为空。
该选项组可以控制GameObject在渲染或烘培流程中的一些行为:
Contribute GI:选中时使物体接收环境光并通过漫反射提供全局光照。
Occluder Static:将物体设置为"遮挡物",当这个物体将某个"被遮挡物"遮挡时(从摄像机角度观察,"被遮挡物"在"遮挡物"后面),让被遮挡物从场景中剔除,效果跟Active为False的时候一致。
Occludee Static:将物体设置为"被遮挡物"。物体可以既是遮挡物又是被遮挡物。
举个例子,假如A B C三个立方体,排成一列, 摄像机在A的正前方,从摄像机的视角中看不到B和C,因为它俩被A挡住了。
如果这时候A是“遮挡物”,B和C是“被遮挡物”。那B和C就不会显示在场景中(不仅是摄像机看不见,而是完全不在场景中出现)。
但如果摄像机穿过A,而移动到B的正前方,B和C都会显示。因为B是“被遮挡物”,却没有被“遮挡物”挡住(此时作为遮挡物的A,在摄像机背面,并没有挡住B).
那C为何会显示呢?因为B只是“被遮挡物”,而不是“遮挡物”,所以它无法遮挡C。摄像机里看不到C,但C依然会显示在场景中。
如果此时把B同时勾选为“遮挡物”。那么C就不会显示了。Batching Static:将物体加入静态批处理队列。GameObject的渲染分为CPU的draw call和GPU的裁剪变形等,如果CPU的draw call数量过多,会出现GPU等待CPU的情况。使用批处理后,在加载该场景的时候,Unity将使用相同材质的物体合并成一个更大的网格来进行draw call,使draw call的效率提升。这个方法是牺牲空间复杂度来满足时间的方法,将导致在加载场景的时候消耗4倍的内存。
注:静态批处理的方法还有在游戏导出的时候,在player setting中勾选static batching,可以在编译阶段预先进行批处理,这样导出来的包就会比较大,但却不会导致额外内存。
Navigation Static:勾选后表示该对象参与AI寻路导航网格的烘培。这个设置将在AI章节中重点解释。
Off Mesh Link Generation:勾选这个选项来自动计算AI寻路导航网路中的超网格链接。这个设置也将在AI章节中讲述。
注:所谓超网格链接,是在两个网格间并不直接相连的情况下,让寻路AI额外考虑的节点:比如两个平面布置了寻路网络,但两个平面间存在一定间隙,正常情况下在平面A上的AI无法通过寻路前往平面B,但布置一个超网格链接,并设置链接类型为"跳跃间隙"后,就可以让AI考虑跳过间隙前往平面B。
Reflection Probe Static:提供静态反射信息,只有勾选这个选项的物体才能被镜面反射。这个设置将在光照章节中讲述。