Unity 2018 AR与VR开发快速上手
上QQ阅读APP看书,第一时间看更新

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方法可以用来解耦合,因为不需要知道指定游戏对象上的具体类就可以运行。但是这种方法效率很低,所以在效率比较重要的场合,还是推荐使用获取指定游戏对象上的脚本组件以后运行相应的方法。