本次依然是siki学院的API学习教程,本章将记录本次学习的总结,并且加上本人的二次理解。
您首先需要明白一件事情,Api是一组公共方法和属性,而Unity官方已经为我们准备好了许多的官方Api方法

思维导图

1.Unity的打印方法

Unity的打印输出主要有print和debug.log两者方法,其中后者的实用更为广泛。其中print是隶属于MonoBehaviour的一个方法,必须是基类为MonoBehaviour的类才可以使用,而debug.log是一个装满了抽象方法的debug类的函数,因此其可以随时随地的使用。
在unity中,打印函数对于玩家是无用的,因为控制台并不能让玩家查看。因此Unity的打印主要用途是用于开发者用来检错和测试,前者常常用来输出各种变量查看问题(应用场景有UI没做完查看人物数组,已经找出经典的null报错源头),后者常常用来替代功能实现的“占位符”(没做完的功能直接用输出一句话来替代,后面再慢慢补充)。

2.事件函数(生命周期函数)的整体调用流程(重要)


Unity中的生命周期顺序图

常用的生命周期函数(已按调用顺序排序):

  • Reset():此函数只能在编辑器模式下(不运行)调用, 当脚本第一次挂载到对象或者使用了Reset命令之后调用,来初始化脚本的各个属性,Reset最常用于在检视面板中提供良好的默认值。(所以可以当一次性场景赋值工具,用了就可以删了)

  • Awake():唤醒事件,游戏一开始运行就执行,只执行一次。

  • OnEnable():激活事件,每当脚本组件被激活的时候就执行一次。

  • Start():开始事件,只执行一次。

  • FixedUpdate():固定更新事件,执行N次,0.02秒执行一次,所有物理组件相关的更新都在这个事件中处理。

  • Update():更新事件,执行每秒的帧数次,每帧执行一次。(所以update本身很不固定,所以移动相关函数要是放在里面要加time函数来保证每秒移动距离相同)

  • LateUpdate():后期更新事件,执行N次,在 Update() 事件执行完毕后再执行。

  • OnGUI():GUI渲染事件,执行N次,执行的次数是 Update() 事件的两倍。

  • OnDisable():失活事件,每当脚本组件失活的时候就执行一次。值得一提的是,物体被摧毁也会导致此函数的执行,并且会在 OnDestroy() 事件前执行

  • OnDestroy():销毁事件,执行一次。当脚本所挂载的游戏物体被销毁时执行

值得注意的一些细节:
Awake()是在脚本对象实例化时被调用的,而Start()是在对象的第一帧时被调用的,因此预制体建议使用Awake()来初始化

在Unity中的C#,实例化的功能大部分被Awake()和Start()这种Unity官方提供的方法替代了。

物体未激活,Awake,OnEnable,Start均不会执行
物体激活,脚本没激活,Awake会执行,OnEnable和Start不会执行
Awake和start在游戏开启后永远只会进行一次执行此后便失去作用,而OnEnable只要激活一次便执行一次

OnEnable和OnDisable两者区别在于一个激活一次调用一次,一个关闭一次调用一次,但是两者的特性基本相同,因此下文用OnEnable包括OnDisable,值得一提的是,上面两者在激活物体的情况下,单独激活关闭脚本即可触发(请正确理解脚本和物体的关系,本质上是脚本驱动,因为物体没激活脚本就是没激活, 物体激活了脚本才激活)

3.游戏物体(GameObject)

Unity是一个Component-Based的引擎,所有的物体资源都是GameObject
GameObject是游戏场景中真实存在的,而且有位置的一个物件,即游戏场景中每一个物体,而每个GameObject一定都拥有Transform(一种组件(Compment),即使它是个空物体),而一个及以上的Compment以不同的功能组合在了一起,构成了GameObject。
而Component附属于GameObject,控制GameObject的各种属性,并且给予组件挂载功能

创建的三种方法
a.使用构造函数(声明+实例化) 创建一个空的游戏对象(不常用)
GameObject myGo = new GameObject(“MyGameObject”);

b.根据现有的预制体(游戏物体)资源或者游戏场景已有的游戏物体来实例化
GameObject.Instantiate(grisGo);

c.使用特别的API创建一些基本的游戏物体类型(原始几何体)
GameObject.CreatePrimitive(PrimitiveType.Plane);

gameobject的查找和获取
a.本体脚本查找:this:指当前组件(通常指脚本,一种compment),而this.gameobject就可以获取该脚本所在的gameobject

b.通过名称查找:GameObject.Find(“Main Camera”);

c.通过标签查找:GameObject.FindGameObjectWithTag(“MainCamera”);

d.通过类型查找: GameObject.FindObjectOfType<No2_EventFunction>();

e.多数查找与获取: GameObject[] enemyGos= GameObject.++FindGameObjectsWithTag++(“Enemy”);
BoxCollider[] colliders= GameObject.++FindObjectsOfType++(); [注意方法的S!!不然就变成上面两个单独查找方法了]

f.碰撞方法查找:即使用方法中的参数就可以获得碰撞的gameobject。

物体的失活和激活
设置:gameObject.SetActive(true);
获取是否激活:gameObject.activeInHierarchy或者gameObject.activeSelf

一些细节
FindObjectsOfType和FindObjectOfType都是从自身开始查找

GameObject是由Component组合成的,Component的生命周期和GameObject息息相关。调用此GameObject的Destroy方法,它的子对象和对应的所有Component都会被销毁,但也可以一次只销毁一个Component
被挂载到 GameObject 下面的脚本会被实例化成 GameObject 的一个成员

对于GameObject一些交互的方法(如GetComponent)已经被Unity官方封装在了GameObject类中,而GameObject直接继承于Object。(Object 是C#中所有类的基类,当然,unity官方对其内部结构有一些改动)。

被显式添加到 Hierarchy(场景物体显示面板) 中的 GameObject 会被最先实例化,GameObject 被实例化的顺序是从下往上。GameObject 被实例化的同时,加载其组件 component 并实例化,如果挂载了脚本组件,则实例化脚本组件时,将调用脚本的 Awake 方法,组件的实例化顺序是也是从下往上。在所有显式的 GameObject 及其组件被实例化完成之前,游戏不会开始播放帧。

4.MonoBehaviour(官方类的继承结构相关)

MonoBehaviour(基类):所有Unity脚本都派生自该基类
Behaviour:是指可启用或禁用的组件(身为MonoBehaviour父类,有控制激活的能力,而它本身又是Compment的子类)

MonoBehaviour派生自组件脚本,因此组件脚本所有的公有,保护的属性,成员变量方法等功能,MonoBehaviour也都有,继承mono之后这类可以挂载到游戏物体上(即没有便无法让这个脚本搭载到gameobject上面)

这里建议搭配官方的方法元数据进行深入理解!

5.Compment(组件)

如果你若需要理解清楚组件的本质,由于Compment本身处于unity场景中的“下层单位”,你必须着重从Unity的层次关系去理解,下列将着重讲述组件在unity中组成层次,以方便理解
组成关系:
一个游戏由多个场景(Scene)组成,一个场景由多个游戏物体(GameObject)组成,一个游戏物体由多个组件(Component)组成
脚本的继承关系
Mono继承自Behaviour,Behaviour继承自Compontent,Compontent继承自Object。
子辈拥有父辈以及父辈以上(派生程度低的基类)所有的公有,保护的属性,成员变量方法等功能,挂载功能其实是Component,也就是我们写的脚本组件其实指的是Component组件而Mono是在此基础上进行的封装与扩展,++但是不要因为unity的全部组件都被称为Compment就认为组件们都是定义在Compment的方法了,实际上,大部分的组件(如Rigdbody,Animator等)都是单独的类,位于UnityEngine让玩家调用++

组件的获取方式和功能

a.组件都是在某一个游戏物体身上挂载的,可以通过游戏物体查找获取之后使用
No5_Component no5_Component = gameObject.GetComponent<No5_Component>();

b.通过其他组件查找
SpriteRenderer sr= grisGo.GetComponent();
sr.GetComponent();
this.GetComponent();

6.Transform (例子涉及到值传递和引用传递)

transform在Unity中也属于组件(Compment),官方名称"变换组件",场景中的每个对象都有一个变换组件。 它用于存储和操作对象的位置、旋转和缩放。(这个方位也许并不是唯一的,有父物体的物体或许拥有本地范围和世界系方位的区别)。并且,transform本身也是最为特殊的一个组件,它拥有以下几个特殊性:
特殊性
a.每个物体都拥有Transform,而且无法摧毁,除非摧毁物体本身

b.可以由 gameobject.transform 或者 脚本.transform 甚至 其他组件.transform 直接获取,而不需要专门动用GetComponent方法

c.本身拥有大量的Api(甚至子类物体统计这种方法不是使用gameobject,而是使用transform),甚至可以使用一些gameobject才有的方法

访问与获取
this.transform (脚本获取)
grisGo.transform (同一个物体上的其他组件获取transform)
gameobject.transform(物体直接获取)

其他一些查找方法
Debug.Log(“当前脚本挂载的游戏对象下的叫Gris的子对象身上的Transform组件是:”+transform.Find(“Gris”));
Debug.Log(“当前脚本挂载的游戏对象下的第一个(0号索引)子对象的Transform引是:”+transform.GetChild(0));
Debug.Log(“Gris当前在此父对象同级里所在的索引位置:”+ grisTrans.GetSiblingIndex());

一些常用成员变量
Debug.Log(“Gris变换组件所挂载的游戏物体名字是:”+grisTrans.name);
Debug.Log(“Gris变换组件所挂载的游戏物体引用是:”+grisTrans.gameObject);
Debug.Log(“Gris下的子对象(指Transform)的个数是:”+grisTrans.childCount);
Debug.Log(“Gris世界空间中的坐标位置是:”+grisTrans.position);
Debug.Log(“Gris以四元数形式表示的旋转是:”+grisTrans.rotation);
Debug.Log(“Gris以欧拉角形式表示的旋转(以度数为单位)是”+grisTrans.eulerAngles);
Debug.Log(“Gris的父级Transform是:”+grisTrans.parent);
Debug.Log(“Gris相对于父对象的位置坐标是:”+grisTrans.localPosition);
Debug.Log(“Gris相对于父对象以四元数形式表示的旋转是:” + grisTrans.localRotation);
Debug.Log(“Gris相对于父对象以欧拉角形式表示的旋转(以度数为单位)是:” + grisTrans.localEulerAngles);
Debug.Log(“Gris相对于父对象的变换缩放是:”+grisTrans.localScale);
Debug.Log(“Gris的自身坐标正前方(Z轴正方向)是:”+grisTrans.forward);
Debug.Log(“Gris的自身坐标正右方(X轴正方向)是:” + grisTrans.right);
Debug.Log(“Gris的自身坐标正上方(Y轴正方向)是:” + grisTrans.up);

一些常用的静态方法
Transform.Destroy(grisTrans);
Transform.Destroy(grisTrans.gameObject);
Transform.FindObjectOfType();
Transform.Instantiate();

Update内常用的函数
a.移动
0.第二个参数不填(实际情况按自身坐标系移动,space.self)
grisGo.transform.Translate(Vector2.leftmoveSpeed);//自身坐标系
grisGo.transform.Translate(-grisGo.transform.right
moveSpeed);//世界坐标系
1.第一个参数按世界坐标系移动,第二个参数指定世界坐标系(实际情况按世界坐标系移动)
grisGo.transform.Translate(Vector2.left*moveSpeed,Space.World);
2.第一个参数按世界坐标系移动,第二个参数指定自身坐标系(实际情况按自身坐标系移动)
grisGo.transform.Translate(Vector2.left * moveSpeed, Space.Self);
3.第一个参数按自身坐标系移动,第二个参数指定世界坐标系(实际情况按自身坐标系移动)
grisGo.transform.Translate(-grisGo.transform.right * moveSpeed, Space.World);
4.第一个参数按自身坐标系移动,第二个参数指定自身坐标系(实际情况按世界坐标系移动)(一般不使用)
grisGo.transform.Translate(-grisGo.transform.right * moveSpeed, Space.Self);
b.旋转
grisGo.transform.Rotate(new Vector3(0,0,1));
grisGo.transform.Rotate(Vector3.forward,1);

值传递和引用传递(C#思辨那里也有所叙述)

   transform.position.x = 4;

上面的句子是不被C#的编译器允许的,因为,position的类型是Vector3的,而Vector3是Struct类型,而结构体的所有值都采用值类型,没有ref的情况下还是值传递,即只传输拷贝的值,而不是一个真正的内存地址,同时,positon被设计成了属性,因此transform.position.x 的全名是transform.positon.get.x,(可以理解为先获得了position这个值类型,然后再获取这个值类型里面带的x这个值)结构体的方法返回的值只是position字段的一个拷贝,因此,对X的修改等于修改拷贝,这将毫无意义,故编译器不予通过
当然,你可以通过公有字段来直接访问结构体的变量,达到获得了变量内存地址进行直接修改的意图。
结论1:用属性和方法返回的结构体是不能修改其字段的
那么,由上面的例子更进一步往下:
EG:

  //结构体 值类型
    public struct MyStruct
    {
        public string name;
        public int age;
    }
    //类 引用类型
    public class MyClass
    {
        public string name;
        public int age;
    }
         //MyStruct myStruct = new MyStruct();//A
        //myStruct.name = "Trigger";
        //myStruct.age = 100;
        ////结论2:直接访问公有的结构体是可以修改其字段的

        //MyStruct yourStruct = myStruct;//B
        //yourStruct.name = "小可爱";
        //yourStruct.age = 18;
        //print("原本的结构体对象名字是:" + myStruct.name);
        //print("修改后的结构体对象名字是:" + yourStruct.name);

        //MyClass myClass = new MyClass();//A
        //myClass.name = "Trigger";
        //myClass.age = 100;
        //MyClass yourClass = myClass;//B跟A是同一块内存空间
        //yourClass.name = "小阔爱";
        //yourClass.age = 18;
        //print("原本的结构体对象名字是:" + myClass.name);
        //print("修改后的结构体对象名字是:" + yourClass.name);

通过对比,可以发现,
1:用属性和方法返回的结构体是不能修改其字段的
2:直接访问公有的结构体是可以修改其字段的
3:用属性和方法返回的类的实例,是可以修改其字段的
总结导致这个问题的原因:
1,Transform中的position是属性(换成方法也一样,因为属性的实现本质上还是方法)而不是公有字段
2,position的类型是Vector的,而Vector是Struct类型
3,Struct之间的赋值是拷贝而不是引用

7.Vector2(系统结构体)

Vector2:用于表示2D向量和点,本身代表的是全局坐标系的方位

Vector2的静态变量:
1.Vector2.down:Y轴反方向
2.Vector2.up:Y轴正方向
3.Vector2.left:X轴反方向
4.Vector2.right:X轴正方向
5.Vector2.one:即(1,1)
6.Vector2.zero:即(0,0)

构造函数:
Vector2 v2 = new Vector2(2, 2);

成员变量:
V2向量的模长是:v2.magnitude);
V2向量的模长的平方是:v2.sqrMagnitude
V2向量单位化之后是:v2.normalized
V2向量的XY值分别是:v2.x 和 v2.y
V2向量的XY值分别是(使用索引器形式访问):v2[0] 和 v2[1]

公共函数
v2.Equals(new Vector2(1, 1))
V2向量与向量(1,1)是否相等

Vector2 v2E = new Vector2(1, 3);
bool equalv2E = v2E.Equals(new Vector2(3, 1));
v2E向量与向量(3,1)是否相等

v2.normalized:V2向量的单位化向量,但是请注意,这个函数并不会改变原来的向量的值

重新设置向量的两种函数办法:
v2.Set(5, 9);

transform.position = v2; (即直接赋新的vector2)

静态函数

 Vector2 va = new Vector2(1, 0);
     Vector2 vb = new Vector2(0, 1);
     //Debug.Log("从va指向vb方向计算的无符号夹角是:" + Vector2.Angle(va, vb));
     //print("va点与vb点之间的距离是:" + Vector2.Distance(va, vb));
     //print("向量va与向量vb的点积是:" + Vector2.Dot(va, vb));
     //print("向量va和向量vb在各个方向上的最大分量组成的新向量是:" + Vector2.Max(va, vb));
     //print("向量va和向量vb在各个方向上的最小分量组成的新向量是:" + Vector2.Min(va, vb));
     ////具体得到的新向量的结果的计算公式是:a+(b-a)*t
     //print("va向vb按照0.5的比例进行线性插值变化之后的结果是" + Vector2.Lerp(va, vb, 0.5f));
     //print("va向vb按照参数为-1的形式进行(无限制)线性插值变化之后的结果是" + Vector2.LerpUnclamped(va, vb, -1));
     //float maxDistance = 0.5f;
     //print("将点va以最大距离不超过maxDistance为移动步频移向vb" + Vector2.MoveTowards(va, vb, maxDistance));
     print("va和vb之间的有符号角度(以度为单位,逆时针为正)是" + Vector2.SignedAngle(va, vb));
     print("vb和va之间的有符号角度(以度为单位,逆时针为正)是" + Vector2.SignedAngle(vb, va));
     //print("va和vb在各个方向上的分量相乘得到的新向量是:" + Vector2.Scale(va, vb));
     //Vector2 currentVelocity = new Vector2(1, 0);
     //print(Vector2.SmoothDamp(va, vb, ref currentVelocity, 0.1f));

运算符
vector2类型的变量可以直接进行一些基本运算,常见的运算如下:

   //print("va加上vb向量是:" + (va + vb));
        //print("va减去vb向量是:" + (va - vb));
        //print("va减去vb向量是:" + va * 10);
        //print("va与vb是同一个向量吗" + (va == vb));

一些特殊的处理移动的方法

       //percent += 1 * lerpSpeed * Time.deltaTime;
       //grisTrans.position = Vector2.Lerp(grisTrans.position, targetTrans.position, percent);
       //lerp是先快后慢,moveTowards匀速
       grisTrans.position = Vector2.MoveTowards(grisTrans.position, targetTrans.position, 0.05f);
       //平滑阻尼       
       //grisTrans.position = Vector2.SmoothDamp(grisTrans.position,targetTrans.position,ref currentVelocity,1);

8.Inpute(系统类)

Inpute:访问输入系统的接口类,用来处理用户的输入

移动常用的一些方法

      ////连续检测(移动)
        //print("当前玩家输入的水平方向的轴值是:"+Input.GetAxis("Horizontal"));
        //print("当前玩家输入的垂直方向的轴值是:" + Input.GetAxis("Vertical"));
        //print("当前玩家输入的水平方向的边界轴值是:" + Input.GetAxisRaw("Horizontal"));
        //print("当前玩家输入的垂直方向的边界轴值是:" + Input.GetAxisRaw("Vertical"));
        //print("当前玩家鼠标水平移动增量是:"+Input.GetAxis("Mouse X"));
        //print("当前玩家鼠标垂直移动增量是:" + Input.GetAxis("Mouse Y"));

常用的玩家按键检测
记得分清楚各个方法的区别!下面的参数名需要进入unity的edit-project settings-input Manager界面进行设置,这些参数通常都对应着按键

        if (Input.GetButton("Fire1"))
        {
            print("当前玩家正在使用武器1进行攻击!");
        }
        if (Input.GetButton("Fire2"))
        {
            print("当前玩家正在使用武器2进行攻击!");
        }
        if (Input.GetButton("RecoverSkill"))
        {
            print("当前玩家使用了恢复技能回血!");
        }
        //间隔检测(事件)
        if (Input.GetButtonDown("Jump"))
        {
            print("当前玩家按下跳跃键");
        }
        if (Input.GetButtonUp("Squat"))
        {
            print("当前玩家松开蹲下建");
        }
        if (Input.GetKeyDown(KeyCode.Q))
        {
            print("当前玩家按下Q键");
        }
        if (Input.anyKeyDown)
        {
            print("当前玩家按下了任意一个按键,游戏开始");
        }
        if (Input.GetMouseButton(0))
        {
            print("当前玩家按住鼠标左键");
        }
        if (Input.GetMouseButtonDown(1))
        {
            print("当前玩家按下鼠标右键");
        }
        if (Input.GetMouseButtonUp(2))
        {
            print("当前玩家抬起鼠标中键(从按下状态松开滚轮)");
        }

9.Message(系统类)

Message:untiy中用来进行物体间的信息发送(主要用来给开发者调试)

用法:

    void Start()
    {
        //仅发送消息给自己(以及身上的其他MonoBehaviour对象)
        gameObject.SendMessage("GetMsg");
        SendMessage("GetSrcMsg","Trigger");
        SendMessage("GetTestMsg",SendMessageOptions.DontRequireReceiver);
        //广播消息(向下发,所有子对象包括自己)
        BroadcastMessage("GetMsg");
        //向上发送消息(父对象包含自己)
        SendMessageUpwards("GetMsg");
    }

    public void GetMsg()
    {
        print("测试对象本身接收到消息了");
    }

    public void GetSrcMsg(string str)
    {
        print("测试对象本身接收到的消息为:"+str);
    }

在上面的代码中,当别的目标物体内也拥有BroadcastMessage或者SendMessageUpwards的参数里面和参数同名的方法,便也会触发,当然,发送者本身也会第一个触发。

10.Animator(组件)

Animator:控制动画播放的组件,需要配合unity自带的动画系统组合使用,下面只展示代码部分

常用的一些方法:

       //animator.Play("Run");  //直接播放对应参数名的动画
        //animator.speed = 0.3f; //设置动画播放速度
        //animator.SetFloat("Speed",1); //修改动画过渡参数,需配合动画系统设置
        //animator.SetBool("Dead",false);//修改动画过渡参数,需配合动画系统设置
        //animator.SetInteger("HP",100); //修改动画过渡参数,需配合动画系统设置
        //print("当前速度参数的值是:"+animator.GetFloat("Speed")); //获得对应参数名的动画过渡参数的值

实战场景:

      void Update()
    {
        if (Input.GetKeyDown(KeyCode.W))
        {
            animator.CrossFade("Walk",1);
        }
        if (Input.GetKeyDown(KeyCode.R))
        {
            //以标准单位化时间进行淡入淡出效果来播放动画(即和动画本身的过渡时间长度对应,比如下面就是50%)
            animator.CrossFade("Run", 0.5f);  
        }
        if (Input.GetKeyDown(KeyCode.Alpha0))
        {
            //以秒为单位进行淡入淡出效果来播放动画
            animator.CrossFadeInFixedTime("Run", 0.5f);
        }
        animator.SetFloat("Speed",Input.GetAxisRaw("Horizontal"));
    } 

11. Time(系统类)

time:该类的方法主要用来处理时间有关的设计

常用方法和变量

  print(Time.deltaTime + ",完成上一帧所用的时间(以秒为单位)");
     print(Time.fixedDeltaTime + ",执行物理或者其他固定帧率更新的时间间隔");
     print(Time.fixedTime + ",自游戏启动以来的总时间(以物理或者其他固定帧率更新的时间间隔累计计算的)");
     print(Time.time + ",游戏开始以来的总时间");
     print(Time.realtimeSinceStartup + ",游戏开始以来的实际时间");
     print(Time.smoothDeltaTime + ",经过平滑处理的Time.deltaTime的时间");
     print(Time.timeScale + ",时间流逝的标度,可以用来慢放动作");
     print(Time.timeSinceLevelLoad + ",自加载上一个关卡以来的时间");

12. Mathf(系统结构体)

Mathf:它的有关方法和变量经常被用来处理各种数学运算

静态变量:

        //print(Mathf.Deg2Rad+",度到弧度换算常量");
        //print(Mathf.Rad2Deg+ ",弧度到度换算常量");
        //print(Mathf.Infinity+"正无穷大的表示形式");
        //print(Mathf.NegativeInfinity + "负无穷大的表示形式");
        //print(Mathf.PI);//即π的值

** 静态函数:**

  //print(Mathf.Abs(-1.2f)+ ",-1.2的绝对值");
        //print(Mathf.Acos(1)+",1(以弧度为单位)的反余弦");
        //print(Mathf.Floor(2.74f)+",小于或等于2.74的最大整数");
        //print(Mathf.FloorToInt(2.74f)+",小于或等于2.74的最大整数");
        ////a+(b-a)*t
        //print(Mathf.Lerp(1,2,0.5f)+",a和b按参数t进行线性插值");        
        //print(Mathf.LerpUnclamped(1, 2, -0.5f) + ",a和b按参数t进行线性插值");

** 一个好用的方法:**
public static float MoveTowards (float current, float target, float maxDelta);
这本质上与 Mathf.Lerp 相同,但是该函数确保速度不超过 maxDelta。 maxDelta 为负值时将值推离 /target/。

eg:

 print("游戏倒计时:" + endTime);
endTime = Mathf.MoveTowards(endTime,0,0.1f); 

13.Random()

random:用来生成随机数的类

静态变量:

print(Random.rotation+",随机出的旋转数是(以四元数形式表示)");
print(Random.rotation.eulerAngles+",四元数转换成欧拉角");
print(Quaternion.Euler(Random.rotation.eulerAngles)+",欧拉角转四元数");
print(Random.value+",随机出[0,1]之间的浮点数");
print(Random.insideUnitCircle+",在(-1,-1)~(1,1)范围内随机生成的一个vector2");
print(Random.state+",当前随机数生成器的状态");

静态函数:

print(Random.Range(0,4)+",在区间[0,4)(整形重载包含左Min,不包含右Max)产生的随机数");
print(Random.Range(0, 4f) + ",在区间[0,4)(浮点形重载包含左Min,包含右Max)产生的随机数");
Random.InitState(1);
print(Random.Range(0,4f)+",设置完随机数状态之后在[0,4]区间内生成的随机数");

14. OnMouseEventFunction鼠标回调事件(MonoBehaivour里的方法)

OnMouseEventFunction:指unity自带的一些和鼠标回调有关的方法,定于于MonoBehaivour中

具体代码

public class No14_OnMouseEventFunction : MonoBehaviour
{
  private void OnMouseDown()
  {
      print("在Gris身上按下了鼠标");
  }

  private void OnMouseUp()  //注意,即使抬起的位置不位于物体也会执行,也就是说本方法的执行和OnMouseDown的位置有关系
  {
      print("在Gris身上按下的鼠标抬起了");
  }

  private void OnMouseDrag() //在拖拽途中会一直执行
  {
      print("在Gris身上用鼠标进行了拖拽操作");
  }

  private void OnMouseEnter() //不用点击,只需移入
  {
      print("鼠标移入了Gris");
  }

  private void OnMouseExit() //不用点击,只需移出
  {
      print("鼠标移出了Gris");
  }

  private void OnMouseOver() //不用点击
  {
      print("鼠标悬停在了Gris上方");
  }

  private void OnMouseUpAsButton() //对OnMouseUp()方法的进阶版,这次对松开位置有要求了
  {
      print("鼠标在Gris身上松开了");
  }
}

15.Coroutine(协程)

Coroutine:协程(协同程序),也属于unity自带的类
用法和用途:1.延时调用,2.和其他逻辑一起协同执行,处理一些主任务之外,比如一些很耗时的工作,在这个协程中执行异步操作,比如下载文件,加载文件等。

协程的开启:
StartCoroutine(“ChangeState”); //不可带参数
StartCoroutine(ChangeState()); //可带参数

协程的关闭:
StopAllCoroutines(); //关闭所有协程
StopCoroutine(“协程方法名”); //关闭所有同参数名的协程
如果想单独关闭一个协程
IEnumerator ie = ChangeState();
StartCoroutine(ie);
StopCoroutine(ie);
可见,单独关闭一个协程必须先把开启的协程设置好引用,然后把引用传入进去
而 StartCoroutine(ChangeState()); StopCoroutine(ChangeState()); 这种方式是无效的,为什么?因为没设定好引用,那即使是同名的协程也可以开启多个,谁知道你想关闭哪个?所以无效。

关于协程内部结构的一些代码:

  IEnumerator ChangeState()
    {
        //暂停几秒(协程挂起)
        yield return new WaitForSeconds(2);
        animator.Play("Walk");
        yield return new WaitForSeconds(3);
        animator.Play("Run");
        //等待一帧 yield return n(n是任意数字)
        yield return null;  
        yield return 100000;  //不管数字多少都是一帧。。。。其实不知道这样的意义是什么
        print("转换成Run状态了");
        //在本帧帧末执行以下逻辑
        yield return new WaitForEndOfFrame();
    }

    IEnumerator CreateGris()
    {
        StartCoroutine(SetCreateCount(5));
        while (true)
        {
            if (grisNum>=grisCount)
            {
                yield break;
            }
            Instantiate(animator.gameObject);
            grisNum++;
            yield return new WaitForSeconds(2);
        }
    }
    IEnumerator SetCreateCount(int num)
    {
        grisCount =num;
        yield return null;
    }

16.Invoke (MonoBehaviour中的方法)

Invoke:延时调用方法,是unity中属于MonoBehaviour的一种方法。和协程的功能有交集,但是协程的功能和灵活性更强,建议搭配协程学习

Invoke方法的调用:

Invoke("Delete",3);//在三秒后执行Delete方法
InvokeRepeating("Print",3,3);//在三秒后执行Print方法,并且每隔3秒重复执行一次

由上面可以看成,比起协程,invoke更方便快速,可以直接使用方法,但是不可以带参数 。 而且功能少的多,不灵活

Invoke方法的停止:
CanceInvoke(string “方法名”): 取消这个脚本中所有的调用 取消这个在MonoBehaviour上的所有调用。

Invoke方法的检验
Invokeing(“方法名”);有返回值返回bool型。正在调用就返回true

17.Rigidbody(刚体)

Rigdbody:在unity中是挂载在物体上的一种模拟物体的组件,本文着重于它的代码实现
在脚本中,它继承了Compment,身为一个类位于UnityEngine中

常用代码

  private Rigidbody2D rb;
  private float moveSpeed=1;
  private float angle=60;

  void Start()
  {
      rb = GetComponent<Rigidbody2D>();
      print(rb.position);
      print(rb.gravityScale+",该对象受重力影响的程度");
      rb.gravityScale = 0;
      //rb.AddForce(Vector2.right*10);
      //rb.AddForce(Vector2.right * 10,ForceMode2D.Impulse);
      rb.velocity = Vector2.right *moveSpeed;
      //rb.MoveRotation(90);
  }

  void FixedUpdate()
  {
      //rb.MovePosition(rb.position+Vector2.right*moveSpeed*Time.fixedDeltaTime);  
      rb.MoveRotation(rb.rotation+angle*Time.fixedDeltaTime);
  }