3.8 脚本
3.8.1 基本介绍
Unity3D是基于Mono项目实现的,而Mono项目可以简单地理解为第三方实现的跨平台的.net framework。
在这里,就不介绍C#语言的基础,读者可以直接查阅微软的资料,基本数据类型、语法、类的操作等不变。这里只介绍与微软C#的不同之处。
在“Project”窗口中单击鼠标右键,依次选择“Create→C# Script”就可以添加脚本组件,如图3-58所示。
图3-58
3.8.2 MonoBehaviour
一个脚本想要成为组件,必须继承MonoBehaviour类。
在脚本继承了MonoBehaviour类之后,公有属性的默认值,可以在Unity编辑器中进行设置。例如添加脚本:
using UnityEngine; public class MonoController : MonoBehaviour { public string str; public Vector3 vector3; public Color color; public GameObject go; public float f1; }
新建一个游戏对象,将脚本拖到该游戏对象下成为组件,如图3-59所示。
图3-59
这时,可以直接在脚本组件上设置公有属性的值,如图3-60所示。
图3-60
3.8.3 Transform属性
Transform属性可以用来设置游戏对象的位置、角度和缩放,等同于在编辑界面修改转换的值。
1.设置游戏对象位置
对于游戏对象的位置,设置对象的transform.position属性即可,代码如下:
this.transform.position = new Vector3 (1f, 2f, 3f);
等效于在编辑器中直接修改Position的值,如图3-61所示。
图3-61
2.设置游戏对象角度
设置对象的transform.eulerAngles属性即可,代码如下:
transform.eulerAngles = new Vector3(45f, 10f, 30f);
等效于在编辑器中修改Rotation的值,如图3-62所示。
图3-62
3.设置游戏对象的缩放
对于游戏对象的缩放,设置对象的transform.localScale属性即可,代码如下:
transform.localScale = new Vector3 (1.5f, 2f, 3f);
等效于在编辑器中修改Scale的值,如图3-63所示。
图3-63
3.8.4 GameObject
GameObject用来控制游戏对象本身,最常用的方法是启用或者禁用游戏对象,代码如下:
gameObject.SetActive(false);
等效于在编辑器中设置游戏对象的禁用/启用选项。当传入值为false时,游戏对象被禁用;当传入值为true时,游戏对象被启用,如图3-64所示。
图3-64
3.8.5 常用事件
● Awake
这个函数总是在任何Start()函数之前、一个预设对象被实例化之后被调用,如果一个游戏对象是非活动的(Inactive),那么在启动期间Awake函数是不会被调用的,直到这个游戏对象是活动的(Active)。
● OnEnable
这个函数只有在游戏对象被启用(Enable)且处于活动(Active)状态才会被调用。会发生在一个MonoBehaviour实例被创建时,例如当一个关卡被加载或者一个带有脚本组件的游戏对象被实例化。
● Start
只要脚本实例被启用了Start()函数就会在Update()函数第一帧画面之前被调用。
● FixedUpdate
FixedUpdate函数经常会比Update函数更频繁地被调用。一帧画面会被调用多次,如果帧率低,那么可能不会在帧之间被调用,调用不受硬件性能影响。所有的图形计算和更新在FixedUpdate之后会立即执行。当在FixedUpdate函数中执行移动计算时,并不需要Time.deltaTime乘以帧率值,这是因为FixedUpdate是按独立于帧率的真实时间来被调用的。
● Update
每一帧都会调用这个函数。对于帧的更新,它是主要的负荷函数,调用次数会随硬件性能的高低而变化。
● LateUpdate
LateUpdate会在Update结束之后调用每一帧,在Update执行结束后LateUpdate开始运行。LateUpdate常用于第三人称视角摄像机的跟随效果。
● OnDisable
当行为变为禁用(Disable)或非活动(Inactive)时调用这个函数。
3.8.6 Instantiate
Instantiate方法(也称为函数)是用来实例化一个预制件的方法,支持泛型,关键代码如下:
public GameObject perfab; public void AddGameObject(){ Instantiate (perfab); }
新建一个预制件,如图3-65所示。将预制件赋值给脚本,如图3-66所示。
图3-65
图3-66
运行效果如图3-67所示,每次单击都会生成一个新的游戏对象。
图3-67
3.8.7 Destroy
Destroy方法用来删除一个游戏对象或者组件。当传入参数的类型是游戏对象时,将删除该游戏对象;当传入参数的类型是非游戏对象时,将删除该组件,如图3-68所示。
图3-68
关键代码如下:
public GameObject obj; public void Del(){ Destroy (obj); }
初始状态如图3-69所示,单击“Destroy”按钮以后如图3-70所示。
图3-69
图3-70
3.8.8 获取指定游戏对象
获取指定游戏对象的基本方法有4种。
1.通过公有属性定义在编辑器中的设置
代码如下:
public GameObject go1;
将要对应的游戏对象拖到属性中,完成赋值操作,如图3-71所示。
图3-71
这样做相当于设置了静态的值,在运行中无法再次修改。好处是,即使处于非活动状态的游戏对象也能赋值。
2.调用GameObject.Find方法找到游戏对象
代码如下:
var go = GameObject.Find("Green");
这个方法是在当前运行场景中寻找名为“Green”的游戏对象,如果有多个,则返回第一个。这个方法不能找到被禁用的游戏对象。输入值是要查找的游戏对象的路径和名称,例如可以是“/GameMaster/Grey/Sand”,如图3-72所示。
图3-72
3.调用transform.Find方法找到游戏对象
代码如下:
var go = transform.Find("Grey").gameObject;
transform.Find方法找到的其实不是gameObject属性的对象,找到的是transform属性的对象。不过任何组件都有gameObject属性和transform属性。
这个方法的缺点是,只能找到指定的Transform下的游戏对象,好处是能够查找到未处于活动状态的游戏对象。输入值是要查找的游戏对象的路径和名称,即可以是“/GameMaster/Grey/Sand”。
4.调用FindWithTag方法找到游戏对象
代码如下:
var go = GameObject.FindWithTag ("Player");
将要查找的游戏对象的“Tag”属性设置为对应值,如图3-73所示。
图3-73
这个方法也不能找到处于非活动状态的游戏对象。
3.8.9 获取指定的组件
获取指定组件的基本方法有3种。
1.通过公有属性定义在编辑器中的设置
代码如下:
public Camera cam;
将要包含对应组件的游戏对象拖到属性中,完成赋值操作,如图3-74所示。
图3-74
这样做相当于设置了静态的值,在运行中无法再次修改。好处是,即使是处于非活动状态的游戏对象也能赋值。
2.调用GetComponent方法找到游戏对象
代码如下:
var audio = GetComponent<AudioSource>();
这个方法用于从指定的Transform对象中获取组件,如果没有具体指定,则在脚本所在的当前游戏对象下获取组件。
3.调用FindObjectOfType方法获取游戏对象
代码如下:
var canvas = FindObjectOfType<Canvas>();
这个方法是从当前场景中获取指定类型的组件。因为每个组件都对应具体的游戏对象,所以这种方法也可以用于获取拥有特定组件的游戏对象。
3.8.10 协程
在Unity中当某些内容需要等待时,可以使用协程。代码如下:
void Start() { Debug.Log("start"); StartCoroutine(WaitTime()); Debug.Log("go on"); } IEnumerator WaitTime(){ Debug.Log("start wait"); yield return new WaitForSeconds(5); Debug.Log("End wait"); }
该程序运行时,Debug.Log("End wait");并不会马上被执行,而是等待时间到了才执行,如图3-75所示。
图3-75
3.8.11 场景切换
切换的场景必须都在Scenes In Build里。
1.直接切换
代码如下:
SceneManager.LoadScene("First");
这时会直接切换到指定场景,但是当指定场景加载比较慢的时候会显示出卡顿的情况,如图3-76所示。
图3-76
2.异步切换
代码如下:
SceneManager.LoadSceneAsync("Second");
异步切换会先加载场景,加载完以后再切换。执行切换以后,仍然可以继续操作,直到加载完成再切换。
3.带进度条的切换
代码如下:
这个其实是异步切换的加强版,通过滚动条来显示加载的进度,这种方法用得最多。
3.8.12 DontDestroyOnLoad
调用DontDestroyOnLoad可以将对象所在的游戏对象保留,当场景切换时不被销毁,代码如下:
DontDestroyOnLoad(gameObject);
当有内容需要在不同的场景中进行传递时,可以将信息挂在一个统一的游戏对象下,然后将这个游戏对象用该方法设置为不会因为场景切换而卸载,实现信息在不同场景中的传递。
3.8.13 SendMessage
SendMessage方法可以用来调用指定游戏对象中脚本组件的方法,无论该方法是否是公有方法。
代码1(见图3-77):
public GameObject go; public void Send(){ go.SendMessage("Show"); }
图3-77
代码2(见图3-78):
private void Show(){ Debug.Log("SMGetController 脚本被执行了。"); }
图3-78
将按钮单击事件设置为Send方法,单击后,就能看到其他脚本的Show方法被执行了,如图3-79所示。
图3-79
SendMessage方法可以用来解耦合,因为不需要知道指定游戏对象上的具体类就可以运行。但是这种方法效率很低,所以在效率比较重要的场合,还是推荐使用获取指定游戏对象上的脚本组件以后运行相应的方法。