Unity基础 MonoBehavior中的重要内容
文章目录
Mono中的重要内容
1、延迟函数
1、延迟函数概念
延时执行的函数
2、延迟函数使用
只有继承MonoBehavior的脚本可以使用延时相关函数
1、延迟函数
Invoke();
参数一:函数名字符串
参数二:延迟时间 秒为单位
注意:
1、Invoke只调用无参方法,若要调用有参,只需将有参方法写在无参方法体中
2、函数名必须是脚本上声明的函数
2、延迟重复执行函数
InvokeRepeating();
参数一:函数名字符串
参数二:第一次执行延迟时间
参数三:每次执行的间隔时间
3、取消延迟函数
取消该脚本的所有延时函数
CancelInvoke();
指定函数名取消
CancelInvoke("函数名");
4、判断是否有延迟函数
if(IsInvokeing()){
print("存在延时函数");
}
if(IsInvokeing("函数名")){
print("存在延时函数函数名");
}
3、延迟函数受对象失活销毁影响
脚本依附对象失活or脚本失活
延时函数可以继续执行
脚本依附对象销毁or脚本移除
延时函数无法继续执行
void OnEnable(){
//对象激活时,开启延迟
}
void OnDisable(){
//对象失活时,停止延迟
}
思考1 利用延时函数实现计时器
方法一:
private int time = 0;
void Start()
{
InvokeRepeating("DelayFunc",0,1);
}
void DelayFunc()
{
print(time+"秒");
time++;
}
方法二:
private int time = 0;
void Start()
{
DelayFunc();
}
void DelayFunc()
{
print(time + "秒");
time++;
Invoke("DelayFunc", 1);
}
思考2 延时销毁
void Start()
{
//Destroy(gameObject,5);
Invoke("DelayDes",5);
}
void DelayDes()
{
Destroy(gameObject);
}
2、协同程序
1、Unity是否支持多线程
Unity是支持多线程的
只是新开线程无法访问Unity相关对象的内容
新开线程主要进行复杂逻辑运算或者网络消息接收等
注意:新开线程后Unity中的多线程要记得关闭
Thread t;
void Start()
{
t = new Thread(Test);
t.Start();
}
void Test()
{
while (true)
{
Thread.Sleep(1000);
print("Test");
}
}
private void OnDestroy()
{
t.Abort();
t = null;
}
2、协同程序概念
1、协同程序简称协程:
它是“假”的多线程,它不是多线程
2、它的主要作用:
将代码分时执行,不卡主线程
简单理解,是把可能会让主线程卡顿的耗时的逻辑分时分步执行
3、主要使用场景:
异步加载文件
异步下载文件
场景异步加载
批量创建时防止卡顿
3、协同程序和线程的区别
1、新开一个线程
是独立的一个管道,和主线程分开执行
2、新开一个协程
是在原线程之上开启,进行逻辑分时分步执行
4、协程的使用
继承MonoBehavior的类都可以开启协程函数
第一步:声明协程函数
协程函数2个关键点
1、返回值为IEnumerator类型及其子类
2、函数中通过 yield return 返回值,进行返回
代码示例:
IEnumerator MyCoroutine(int i, string str)
{
yield return new WaitForSeconds(5);
}
第二步:开启协程函数常用开后启方式
StartCoroutine(MyCoroutine(1,"abc"));
第三步:关闭协程
关闭所有协程
StopAllCoroutines();
关闭指定协程
Coroutine c = StartCoroutine(MyCoroutine(1,"abc"));
StopCoroutine(c);
5、yield return 不同内容的含义
1.下一帧执行
yield return 数字;
yieldreturn null;
在Update和LateUpdate之间执行
2.等待指定秒后执行
yield return new WaitForSeconds(秒);
在Update和LateUpdate之间执行
3.等待下一个固定物理顿更新时执行
yield return new WaitForFixedUpdate();
在FixedUpdate和碰撞检测相关承函数之后执行
4.等待摄像机和GUI染完成后执行(用来截图)
yield return new WaitForEndOfFrame();
在LateUpdate之后的染相关处理完毕后之后
5.一些特殊类型的对象 比如异步加载相关函数返回的对象
在之后【异步加载资源】【异步加载场景】【网络加载】里
一般在Update和LateUpdate之间执行
6.跳出协程
yield break;
6、协程受对象和组件失活销毁的影响
协程开启后
脚本组件和物体【销毁】,协程不执行
物体【失活】协程不执行
只有在脚本组件失活时协程执行
思考1 利用协程制作一个计时器
//开启协程
void Start()
{
StartCoroutine(MyCoroutine());
}
//声明协程
IEnumerator MyCoroutine()
{
int time = 0;
while (true)
{
print(time + "秒");
time++;
yield return new WaitForSeconds(1);
}
}
思考2 利用协程创建十万立方体
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
StartCoroutine(CreateCube(10000));
}
}
IEnumerator CreateCube(int num)
{
for (int i = 0; i < num; i++)
{
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.transform.position = new Vector3(Random.Range(-100, 100), Random.Range(-100, 100), Random.Range(-100, 100));
if (i % 100 == 0)
{
yield return null;
}
}
}
3、协同程序原理
1、协程的本质
协程可以分成两部分
1.协程函数本体
2.协程调度器
协程本体就是一个能够中间暂停返回的函数
协程调度器是Unity内部实现的,会在对应的时机帮助我们继续执行协程函数
Unity只实现了协程调度部分
协程的本体本质上就是一个C#的迭代器方法
2、协程的本体是迭代器方法的体现
1、协程函数本体
如果我们不通过开启协程方法执行协程
Unity的协程调度器是不会帮助我们管理协程函数的
但是我们可以自己执行迭代器函数内容
void Start()
{
IEnumerator ie = Test(); //手动开启
ie.MoveNext(); //得到第一个yield return前的内容
print(ie.Current); //得到yield return的内容
//循环获取内容
while (ie.MoveNext())
print(ie.Current);
}
IEnumerator Test()
{
print("第一次迭代内容");
yield return "测试";
}
2.协程调度器
继承MonoBehavior后 开启协程
相当于是把一个协程函数(选代器)放入Unity的协程调度器中帮助我们管理进行执行
具体的yield return后面的规则也是Unity定义的一些规则
总结
你可以简化理解迭代器函数
C#看到迭代器函数和yield return语法糖
就会把原本是一个的函数变成“几部分”!
我们可以通过迭代器从上到下遍历这“几部分”进行执行
就达到了将一个函数中的逻辑分时执行的目的
而协程调度器就是利用送代器函数返回的内容来进行之后的处理
比如Unity中的协程调度器
限据yield return返回的内容决定了下一次在何时继续执行代器函数中的下一部分
理论上来说我们可以利用迭代器函数的特点自己实现协程调度器来取代unity自带的调度器
总结
协程的本质就是利用
C#的迭代器函数【分步】执行的特点加上
协程调度逻辑
实现的一套【分时】执行函数的规则
思考 通过迭代器自定义协程
请不使用Unity自带的协程协调器开启协程
通过迭代器函数实现每隔一秒执行函数中的一部分逻辑
CoroutineMgr
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class YieldReturnTime
{
//每一次执行的迭代器接口
public IEnumerator ie;
//下一次执行的时间
public float time;
}
public class CoroutineMgr : MonoBehaviour
{
private static CoroutineMgr instance;
public static CoroutineMgr Instance => instance;
//声明用于存储迭代器对象的容器
private List<YieldReturnTime> yrtList = new List<YieldReturnTime>();
void Awake()
{
instance = this;
}
public void MyStartCoroutine(IEnumerator ie)
{
//分步分时逻辑
if (ie.MoveNext())
{
if (ie.Current is int)
{
YieldReturnTime yrt = new YieldReturnTime();
//记录迭代器和时间
yrt.ie = ie;
yrt.time = Time.time + (int)ie.Current;
yrtList.Add(yrt);
}
}
}
void Update()
{
//判断预期时间,若正常时间>=预期时间,则执行下一步
//倒着遍历,目的是防止遍历时迭代器移除内容发生前移,产生漏读
for (int i = yrtList.Count - 1; i >= 0; i--)
{
if (yrtList[i].time <= Time.time)
{
if (yrtList[i].ie.MoveNext())
{
if (yrtList[i].ie.Current is int)
yrtList[i].time = Time.time + (int)yrtList[i].ie.Current;
}
else
{
yrtList.RemoveAt(i);
}
}
}
}
}
Xiecheng
using System.Collections;
using UnityEngine;
public class Xiecheng : MonoBehaviour
{
void Start()
{
CoroutineMgr.Instance.MyStartCoroutine(MyCoroutine());
}
IEnumerator MyCoroutine()
{
print("A");
yield return 1;
print("B");
yield return 1;
print("C");
yield return 1;
print("D");
yield return 1;
}
}