文章目录
- 6.1 组件的容器——GameObject
- 6.2 创建物体
- 6.2.1 创建空物体——Create Empty
- 6.2.2 创建几何体物体
- 6.2.3 用代码动态创建物体
- 6.3 GameObject常用方法
- 6.3.1 添加组件,AddComponent
- 6.3.2 获取组件,GetComponent
- 6.3.3 通过标签查找物体,FindWithTag
- 6.3.4 通过名字查找物体,Find
- 6.4.5 实例化游戏对象,Instantiate
- 6.4.6 销毁游戏对象,Destroy
简介:我是一名Unity
游戏开发工程师,皮皮是我养的猫,会讲人话,它接到了喵星的特殊任务:学习编程,学习Unity
游戏开发。
于是,发生了一系列有趣的故事。
6.1 组件的容器——GameObject
皮皮:“铲屎官,我有一个问题没想明白,为什么之前我们每次都是把脚本挂到Main Camera
上,是只有Main Camera
才能运行脚本吗?”
我:“当然不是呀,只是因为空工程中,场景里就只有一个Main Camera
和一个Directional Light
,为了测试脚本,就随便挂到Main Camera
上了。事实上,我们也可以把脚本挂到Directional Light
上测试之前的脚本,当然,我们还可以自己创建GameObject
。”
皮皮:“什么是GameObject
呀。”
我:“GameObject
就是游戏对象的意思,在Unity
中,所有实体都是游戏对象,比如摄像机Main Camera
、直射光Directional Light
,它们其实都是GameObject
,你可以把GameObject
理解为一个容器,不同能力的组件装在这个容器里,容器便具有了相应的能力,比如挂了Camera
组件的GameObject
,它便具备了摄像机的能力,挂了Light
组件的GameObject
,它便具备了灯光的能力。”
皮皮:“原来如喵,我懂了。那怎么创建GameObject
呢?”
我:“来,看我变魔术。”
6.2 创建物体
6.2.1 创建空物体——Create Empty
在Hierarchy
视图中右键点击菜单Create Empty
,即可创建一个空物体。
默认空物体的名字为GameObject
。
我们可以对它进行重命名,选中物体后,在Inspector
最上方的输入栏中即可重新输入物体的名字,输入后按回车即可生效,比如我这里将物体重命名为EmptyGameObject
。
皮皮:“为什么这个EmptyGameObject
在Game
视图中看不见?”
我:“问得好,但我先不回答你这个问题,我们先来创建一个看得见的GameObject
。”
6.2.2 创建几何体物体
在Hierarchy
视图中右键点击菜单3D Object - Cube
。
此时场景中出现了一个正方体Cube
。
这回,我们看见了,一个看得见的GameObject
。
我:“老皮,你自己观察观察,这个Cube
和我们刚刚创建的空物体EmptyGameObject
有什么不同?”
皮皮:“我发现了,Cube
身上比EmptyGameObject
多了好几个组件。一定是这些组件让Cube
具有被看得见的能力。”
我:“如喵可教也,一点即通。那你再观察观察,是什么让它的外形是正方体,而不是一个球体或者别的呢?”
皮皮用鼠标点来点去,点开了Mesh
的选择框。
皮皮:“原来秘密藏在这里呀。”
我:“不错不错,Mesh
就是网格的意思,你可以理解为骨架,我们可以选择别的Mesh
,比如球体Sphere
,这样它的外形就变成一个球啦。”
6.2.3 用代码动态创建物体
我:“刚刚我们都是手动创建物体的,有时候我们需要用代码动态创建物体。”
皮皮:“看到代码就犯困。”
我:“不要忘记你们喵星人的使命。其实很简单,一行代码就可以实现。”
创建一个脚本CreateGameObjectTest
。
代码如下:
using UnityEngine;
/// <summary>
/// 测试使用代码动态创建物体
/// </summary>
public class CreateGameObjectTest : MonoBehaviour
{
void Start()
{
// 创建一个名字为EmptyGo的空物体
var go = new GameObject("EmptyGo");
}
}
把CreateGameObjectTest
脚本挂到Main Camera
上,运行Unity
,可以看到动态创建出了一个EmptyGo
物体了。
皮皮:“你上面是用代码动态创建一个空物体,那如果我想动态创建几何体呢?”
我:“Unity
已经帮我们封装好接口了,使用GameObject.CreatePrimitive
接口。”
例:
// 代码动态创建正方体
var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
其中PrimitiveType
是枚举类型,传对应的枚举值即可创建不同的几何体。
public enum PrimitiveType
{
// 球体
Sphere = 0,
// 胶囊体
Capsule = 1,
// 圆柱体
Cylinder = 2,
/// 正方体
Cube = 3,
// 由很多个三角形组成的平面
Plane = 4,
// 由两个三角形组成的平面
Quad = 5
}
皮皮:“看出来了,它只能创建一些简单的几何体形状,如果我想创建一个很复杂的几何体呢?”
我:“那就要建模软件在外部制作好然后再导入Unity
中,包装成预设,然后通过预设实例化的方式来啦。”
皮皮:“太酷了,我要学建模。”
我:“你就是小学课本里那只钓鱼的猫,看见蝴蝶就去抓,结果鱼没钓到,蝴蝶也没抓到。你要耐下性子,后面有时间我再教你建模。”
皮皮:“铲屎官怎么啥都会!”
我:“艺多不压身,功到自然成。”
6.3 GameObject常用方法
GameObject
是我们在Unity
中最常打交道的一个类,所以我们需要熟悉它的接口和属性。
6.3.1 添加组件,AddComponent
GameObject
可以理解为是组件的容器,通过AddComponent
接口,可以动态给GameObject
添加组件。
public Component AddComponent(Type componentType);
public T AddComponent<T>() where T : Component;
例:
// 添加BoxCollider组件
BoxCollider boxCollider = gameObject.AddComponent<BoxCollider>();
皮皮:“这个小写g
开头的gameObject
又是哪里来的呀?”
我:“在MonoBehaviour
(包括子类)脚本中,小写g
开头的gameObject
表示组件所挂的物体本身。”
6.3.2 获取组件,GetComponent
相应的,我们可以通过GetComponent
来获取GameObject
上的组件。
public Component GetComponent(string type);
public T GetComponent<T>();
public Component GetComponent(Type type);
参数为
string
类型的GetComponent
接口尽量少用,因为它用到了反射,第一是反射执行效率低,第二是字符串容易写错。
例:
// 获取BoxCollider组件,如果没有,则返回null
BoxCollider boxCollider = gameObject.GetComponent<BoxCollider>();
这个接口是从自身节点获取某个组件,找到一个则立即返回。
除此之外,还有如下一些接口:
接口 | 说明 |
GetComponentInChildren | 从子节点获取某个组件,找到一个则立即返回 |
GetComponentsInChildren | 从子节点获取某个组件,返回一个组件数组 |
GetComponentInParent | 从父节点获取某个组件,找到一个则立即返回 |
GetComponentsInParent | 从父节点获取某个组件,返回一个组件数组 |
GetComponents | 从自身节点获取某个组件,返回一个组件数组 |
例:
// 从子节点获取BoxCollider组件,有多少个就返回多少个,参数true表示即使子节点不激活也照样查找
BoxCollider[] boxColliderArray = gameObject.GetComponentsInChildren<BoxCollider>(true);
6.3.3 通过标签查找物体,FindWithTag
物体是可以打标签的,选中物体,在Inspector
窗口中的Tag
属性下拉框中即可设置。
要新增Tag
可以点击Add Tag...
。
在Tags
框中点击+
号,即可添加Tag
,如下,添加了一个MyTag
。
将物体的Tag
设置为MyTag
。
FindWithTag
接口,通过标签在场景中查找物体,找到一个则立刻返回。
// 通过标签查找物体
public static GameObject FindWithTag(string tag);
例:
GameObject target = GameObject.FindWithTag("MyTag");
如果有多个物体具有相同的Tag
,我们想查找所有物体,则要用另一个接口:
public static GameObject[] FindGameObjectsWithTag(string tag);
皮皮:“我怎么通过代码获取物体的Tag
呢?”
我:“GameObject
提供了tag
属性。”
// 获取物体的tag
string tag = gameObject.tag;
皮皮:“那我们可以通过tag
属性设置物体的Tag
吗?”
我:“可以的。不过前提是设置的Tag
需要存在,不能设置一个不存在的Tag
,否则会报错的。”
// 设置物体的tag
gameObject.tag = "MyTag";
我:“还有,虽然GameObject
提供了这个tag
属性,但也不要泛滥使用,只有少数必要的情况才会使用Tag
,具体后面实战项目我再演示吧。”
6.3.4 通过名字查找物体,Find
通过名字在场景中查找物体,找到一个则立刻返回,所以如果有多个物体具有相同的名字,这个时候就不大好区分了。
public static GameObject Find(string name);
该方法建议少用,因为场景中同名的物体概率还是比较大的。
另外,我们可以通过name
属性获取当前物体的名字,如下:
// 获取当前游戏对象的名字
string name = gameObject.name;
皮皮:“也可以通过name
属性修改物体的名字,对吗?”
我:“聪明。”
// 设置物体的名字
gameObject.name = "NewName";
6.4.5 实例化游戏对象,Instantiate
这个接口应该不陌生了,实例化可以理解为一次深拷贝,或者叫克隆。
public static Object Instantiate(Object original);
这个接口是Object
类的,因为GameObject
是继承Object
,所以就有这个接口了。
6.4.6 销毁游戏对象,Destroy
在拍戏的时候,演员的角色剧情演完之后就杀青了,游戏对象也类似,它完成了它的任务后,就要被销毁或者对象池回收。
这个接口是Object
类的,因为GameObject
是继承Object
,所以就有这个接口了。
public static void Destroy(Object obj);
皮皮:“什么是对象池,怎么回收游戏对象?”
我:“好奇猫,后面再教你,你先学好今天的内容。”
《学Unity的猫》——第七章:Transform的魔力,超越光速的移动