唐僧 发表于 2020-11-24 10:12

Unity脚本基础

一:Unity脚本概述以及断点调试

二:Time等常见工具类的使用

三:Unity脚本生命周期函数

四:对象基本操作

五:本章总结

一:Unity脚本概述以及断点调试

       与其它常用的平台有所不同,Unity中的脚本程序如果要起作用,主要途径为将脚本挂载到特定的对象上。这样,脚本中的方法在特定的情况下就会被回调,实现特定的功能。
       其实上节所说的几个回调方法一般是位于MonoBehavior类的子类中的,也就是说开发脚本代码时,主要是继承自MonoBehavior类并重写其中特定的方法。
       用于初始化脚本的代码必须置于Awake或者Start方法中。两者的不同之处在于,Awake方法是在加载时运行,Start方法是场景加载后第一次调用Update之前调用。因此Awake方法总是在Start方法前调用。不要在构造函数中初始化任何变量,要用Awake或者Start方法来实现。即便是在编辑模式,Unity仍然会自动调用构造函数,这通常是在一个脚本编译之后,因为需要调用脚本的构造函数来取回脚本的默认值。无法预计何时调用构造函数,它或许会被预制件或未激活的游戏对象所调用。
       通过观察上节的代码可以看出,只有是public类型的变量才能在检视视图中通过拖拽来完成赋值操作,而private或者protected类型的成员变量不能查看。当然,后期编辑器扩展是特例。
       Unity中C#代码的调试与传统的C#调试有所不同。Unity有一个控制台,Console,在这个控制台可以看到脚本中精确的错误,包括位置、行数。比如以下错误代码:
using UnityEngine;
using System.Collections;
public class TestDebug : MonoBehaviour {
        public int num;
        // Use this for initialization
        void Start () {
                num = 0.2f;
        }
        // Update is called once per frame
        void Update () {
        }
}       print ("快速输出");

       使用Print也能进行快速输出,但是输出的时候,只能在Mono类中。Debug.Log可以在所有类使用。


       断点调试功能可谓是程序员必备的功能了。Unity3D支持编写js和c#脚本,但很多人可能不知道,其实Unity3D也能对程序进行断点调试的。不过这个断点调试功能只限于使用Unity3D自带的MonoDevelop编辑器。
  使用UnityVS可以使用VS对Unity进行断点调试,我们这里首先讲解第一种调试方式。调试具体步骤:
(1)首先把编辑器指定为自带的MonoDeveloper。点击Preference—>ExternalTools,选择所需的编辑器即可。
(2)新建C#脚本,我们使用OnGUI新建两个按钮,点击按钮进行测试输出。代码如下所示:
        public class TestBreak : MonoBehaviour {
                private int sum=0;
                void OnGUI()
                {
                if (GUILayout.Button ("add")) {
                        for (int i = 0; i < 10; i++) {
                                sum+=i;
                                Debug.Log("sum="+sum);
                        }
                }
                if (GUILayout.Button ("Clear")) {
                        sum=0;
                }
        }
}(3)写好脚本之后,把脚本随便拖到场景的物体上面,我们准备调试了。在调试之前,我们需要把MonoDevelop和Unity3D进行连接。
       连接方式如下图所示,点击运行—->AttachToProcess,选择需要连接的进程。如下图所示:
      选择UnityEditor,点击Attach。这时候,MonoDevelop就连接上了Unity了。
   当连接好了Unity3D之后,右边的调试按钮就变成可用了。我们在代码里面添加断点。
(4)回到Unity,点击Run按钮,此时程序开始运行。点击其中的Button。
(5)此时Unity会跳转到MonoDevelope界面,如下所示:
(6)上方的四个按钮分别表示继续执行,单步执行逐过程,单步执行逐语句,跳出循环。
(7)调试完之后,我建议最好把MonoDevelop和Unity3D的连接断开。假如你不断开的话,Unity会一直处于很卡的状态。
  断开的方法也是有几种了,首先对应Debug按钮有一个断开的按钮,可以直接点击断开。或者点击运行——->Detach即可。
宏观控制类
Application —— 应用程序类
Input —— 输入类
GUI —— 图形输出类
Physics —— 物理引擎类
Resources —— 资源类
Time —— 时间类
游戏对象(GameObject)类
       由于Unity是面向组件的开发模式(而非面向对象),所以从类的继承关系中也能看出,对于GameObject类没有下太多的笔墨,而是将更多的内容交给下面的组件类。
组件(Component)类
       组件(Component)是用来绑定到游戏对象(Game Object)上的一组相关属性。本质上每个组件是一个类的实例。常见的组件有:MeshFilter、MeshCollider、Renderer、Animation等等。
       其实不同的游戏对象,都可以看成是一个空的游戏对象,只是绑定了不同的组件。比如:Camera对象,就是一个空对象,加上Camera、GUILayer、FlareLayer、AudioListener等组件。其他对象绑定的组件,可自行观察。   
       开发的时候,我们使用最多的就是Unity组件类以及自定义的组件脚本。
Unity基础常见类关系图:
二:Time等常见工具类的使用

(1)Unity提供了Time类,这个类主要用来获取当前的系统时间。其中常见的几个属性如下所示:
Time.time:从游戏开始后开始计时,表示截止目前共运行的游戏时间。Time.deltaTime:获取Update()方法中完成上一帧所消耗的时间。Time.fixedTime:FixedUpdate()方法中固定消耗的时间总和。FixedUpdate()每一帧更新的时间可以通过导航菜单栏”Edit“——”Project Settings“——”Time“菜单项去设置。Time.fixedDeltaTime:固定更新上一帧所消耗的时间。Time.timeScale:时间比例,等于0表示暂停,=1表示正常,大于1表示加速,小于1表示减速。只影响FixedUpdate,不影响Update和LateUpdate。
问题:如何让一个对象匀速运动?匀速旋转?
(2)随机数:在开发中,有时需要获取程序中的随机数,这可以使用Random.Range()方法实现,其中该方法的第一个参数为随机数的起始位置,第二个参数为获取的随机数的结束位置。
(3)Unity开发中封装了一个数学类Mathf,使用它可以很轻松地帮我们解决复杂的数学公式。
三:Unity脚本生命周期函数

       若是使用脚本函数,我们必须熟练的掌握脚本函数的执行顺序。我们通过下面的例子来学习脚本的生命周期函数。
public class LifeTest : MonoBehaviour {
        void Reset()
        {
                print ("Reset");
        }
        void Awake(){
                print ("Awake");
        }
        void OnEnable()
        {
                print ("Enabled");
        }
        // Use this for initialization
        void Start () {
                print ("Start");
        }
       // Update is called once per frame
        void Update () {
                print ("update");
        }
        void FixedUpdate()
        {
                print ("fixed");
        }
        void OnDisable()
        {
                print ("disabled");
        }
        void OnDestroy()
        {
                print ("destroy");
        }

      大体可以分为以下五个阶段:
      编辑阶段Reset->开始阶段Awake-OnEnable-Start->迭代更新阶段FixedUpdate-Update-LateUpdate->显示阶段OnGUI->清除阶段OnDisable-OnDestroy
四:对象基本操作

4.1:对象查找操作

         通过案例依次使用每个函数。掌握每个函数的具体用法。
         何为Tag?
         Tag是Unity为了标识一个对象而设计的,通过Tag值可以对具体的对象进行管理和筛选,比如碰撞检测的时候,可以通过Tag值过滤一些没必要发生碰撞的物体。如何修改Tag值呢?
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour {
        // Use this for initialization
        void Start () {
                GameObject[] array = GameObject.
                        FindGameObjectsWithTag ("MyTag");
                print (array.Length);
                GameObject one=GameObject.FindGameObjectWithTag ("MyTag");
                print (one.name);       
      SayHello sayHello= GameObject.FindObjectOfType<SayHello>();
      Debug.LogFormat ("{0}{1}",sayHello.gameObject.name,
                                 sayHello.gameObject.tag);

      SayHello[] says = GameObject.FindObjectsOfType<SayHello> ();
                foreach (var item in says) {
                        Debug.LogFormat ("{0}{1}",item.gameObject.name,
                                         item.gameObject.tag);
}
}
       何为Layer?
       在Unity中用int32数据类型来表示32个Layer层。int32表示二进制一共有32位(0—31)。在Unity中每个GameObject都有Layer属性,默认的Layer都是Default。在Unity中可编辑的Layer共有24个(8—31层),官方已使用的是0—7层,默认不可编辑。
       在Unity中使用LayerMask:
       Layers通常被摄像机用来渲染部分场景,和灯光照射部分场景使用。但是它们也可以用来做射线检测时忽略一些collder或Collision使用。
       如何编辑Layers.
       在代码中使用时如何开启某个Layers?答案是正确使LayerMask。
       LayerMask mask = 1 << 你需要开启的Layers层。
       LayerMask mask = 0 << 你需要关闭的Layers层。
       例如:
       LayerMask mask = 1 << 2; 表示开启Layer2。
       LayerMask mask = 0 << 5;表示关闭Layer5。
       LayerMask mask = 1<<2|1<<8;表示开启Layer2和Layer8。
       LayerMask mask = 0<<3|0<<7;表示关闭Layer3和Layer7。
void Update ()
{
    if (Input.GetMouseButtonDown(0)) {
      // 只检测第6层
      LayerMask mask1 = 1 << 6;
      // 只不检测第7层
      LayerMask mask2 = ~(1 << 7);
      // 只检测第8层和第9层
      LayerMask mask3 = (1 << 8)|(1 << 9);

      Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
      RaycastHit hit;
      if (Physics.Raycast(ray, out hit, mask1)) {
            Debug.Log("test");
      }
    }

}
4.2:组件查找操作

public class ComponentTest : MonoBehaviour {
        public GameObject obj;
        // Use this for initialization
        void Start () {
                //this.GetComponent<SayHello> ();
                //1
                SayHello h = obj.GetComponent<SayHello> ();
                h.Hello ();
                //2
                SayHello h1 = obj.GetComponentInChildren<SayHello> ();
                h1.Hello ();
                //3
                SayHello[] h2 = obj.GetComponentsInChildren<SayHello> ();
                print (h2.Length);

                SayHello[] h3= obj.GetComponents<SayHello>();
                print (h3.Length);
        }

给物体添加组件的几种方式:
编辑器直接添加,不再多说通过代码AddComponent添加
if (!ball1.GetComponent<CharacterController> ()) {
                        ball1.AddComponent<CharacterController> ();
                }
通过RequireComponent添加,在脚本添加下面一行代码即可,当把该脚本拖动到物体上时候,会自动为该物体添加刚体组件。

在该脚本内部添加一句代码,那么此时,在Unity菜单栏下面会出现BodyTest菜单,点击即可添加该脚本。
Rigidbody[] body=GetComponentsInChildren<Rigidbody> ();
print (body.Length);
print (body.name);
4.3:自定义脚本查找操作

       自定义一个脚本,提供一个public函数。
using UnityEngine;using System.Collections;public class ChangeMat : MonoBehaviour {
        public void ChangeMatrial(GameObject obj,Color col){
                obj.GetComponent<MeshRenderer> ().material.color = col;
        }
}
public class TestChange : MonoBehaviour {
        // Use this for initialization
        void Start () {
                ChangeMat c = this.GetComponent<ChangeMat> ();
                if (c == null) {
                        c = this.gameObject.AddComponent<ChangeMat> ();
                }
                c.ChangeMatrial (this.gameObject,Color.blue);
        }
}
4.4:层级关系查找游戏对象

       在层级视图的对象列表中,存在有父子关系,在代码中可以通过获取Transform组件来找到子对象或者父对象。
using UnityEngine;
using System.Collections;
public class Cengci : MonoBehaviour {
        private Transform objT;
        // Use this for initialization
        void Start () {
      //transform.Find获取的是名称为Cube的子对象
                objT = this.transform.Find(“Cube”);
                  }
        // Update is called once per frame
        void Update () {
                objT.Rotate(20*Time.deltaTime,0,0);
                objT.parent.Translate(1,0,0);//objT的父对象一直移动
        }
}
       一旦获取到”Cube”子对象,就可以通过GetComponent方法获取到”Cube”子对象的所有组件。另外,Unity中可以通过遍历transform对象来获取其子对象,遍历代码如下:
foreach(Transform child in transform){
                        child.Translate(0.1f,0.1f,0);
}
4.5:代码创建游戏对象

        GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
        cube.AddComponent<Rigidbody>();
        cube.transform.position = new Vector3(1, 1, 0);
//我们将obj1初始化为一个Cube立方体,当然我们也可以初始化为其他的形状
GameObject obj1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
//设置物体的位置Vector3三个参数分别代表x,y,z的坐标数
obj1.transform.position = new Vector3(1,1,1);
//给这个创建出来的对象起个名字
obj1.name = "cube";
//设置物体的tag值,在赋值之前要在Inspector面板中注册一个tag值
//注册tag值得方法,用鼠标选中摄像机对象在Inspector面板中找到tag,选addtag
p> obj1.tag = "shui";</p> //设置物体贴图要图片文件放在(Resources)文件夹下,没有自己创建
obj1.renderer.material.mainTexture = (Texture)Resources.Load("psb20");
五:本章总结

       本章我们主要对Unity中的脚本进行了一个系统的介绍,主要需要大家掌握脚本的生命周期、常见工具类的使用、Unity组件机制、Unity对象查找规则等。
页: [1]
查看完整版本: Unity脚本基础