Unity中的异步编程【1】—— Unity与async 、 await

新手在Unity里写东西,一个方法的内容如果写复杂了,容易把Uinty写死,就会卡帧,用流行的话来总结就是:在Update里面活生生把天聊死了。

此外,如果新手才入门,不擅长使用消息、事件来进行异步的统筹,一门心思在Update里面实现各种有延时有顺序的步骤,于是自己把自己绕晕不说,程序又难调试,难修改,更甚——帧率低下…

那么用协程来实现异步…聪明…

但,还有比协程更好用的异步框架,那就是UniTask


一、什么是异步:async和await是啥

异步这个概念一言难尽,里面涉及到很多上古时候的知识:单核/多核,单线程/多线程,并发/并行,阻塞/非阻塞…

举一个例子:
小赵夫妻响应祖国的号召,夫妻二人7年之内哗啦啦生了3个娃娃。对这对夫妻来说,他们生娃只能一胎一胎的生,所以小赵家生三娃这件事情就是单线程,而且是阻塞式的。
又过了几年,人们纷纷进城,村里的人越来越少,于是大家响应号召要多多生娃,特别是赵庄的赵太爷在县太爷面前拍了胸脯:2年之内,我们赵庄要增加100个新生儿。
于是赵太爷在村里做了动员,大伙儿也积极响应,纷纷备孕。于是赵庄生100个娃这件事情变成了多线程,而且是非阻塞的(大家一起生——这叫多线程,张家怀孕不会影响李家怀孕——这叫非阻塞)。

二、C#(.Net)中的异步

.Net自C#5开始,大概2012年前后,开始引入了异步编程(async和await),有了异步之后,很少需要手动去开线程了,而且业务更好理解,代码更加清晰。

同步举例:解放前的辛家庄有十户人,他们共用一个灶做饭,于是只能轮着顺序做饭…

Unity中的异步编程【1】—— Unity与async 、 await_第1张图片
以上做饭的总耗时为10

代码清单

using System;
using System.Threading.Tasks; //关键的包

public static void Cooking(int i ){
    Console.WriteLine($"第{i}户家庭开始做饭...");     
    Task.Delay(1000).Wait();
    Console.WriteLine($"第{i}户家庭做饭结束");    
}

public static void TestCooking(){
    for(int i = 0;i<10;i++)
    {
        Cooking(i);
    }
}

TestCooking();

异步举例:现在他们富裕了,每家都有一个灶,于是做饭的时候全村炊烟袅袅,大家各做各的,这做饭就成了并发(并行)。

张家做饭和李家做饭和不干涉,互不阻碍。

以下是他们十家一起做饭的壮观情景:
请注意红框中的【async】和【await】关键字
Unity中的异步编程【1】—— Unity与async 、 await_第2张图片
各家在各家做饭,虽然同一时间开始做,但总耗时为1

代码清单

public static async Task Cooking(int i ){
    Console.WriteLine($"第{i}户家庭开始做饭...");     
    await Task.Delay(1000);
    Console.WriteLine($"第{i}户家庭做饭结束");    
}

public static async Task TestCooking(){
    var allTasks = new List<Task>();
    for(int i = 0;i<10;i++)
    {
        allTasks.Add(Cooking(i));
    }
    
    await Task.WhenAll(allTasks.ToArray());
}

await TestCooking();

三、 Unity中的异步

  • 问题的提出:点了按钮,隔三秒启动发动机,发动机转4秒,然后停机。以上内容把它写在一个方法里面。
    解决办法1:一般大家能够想到的就是协程,没问题,这是Unity标准玩法,但是协程没有返回值,也不能好好调试…

解决办法2:聪明的你肯定想到.NET的 async和await了,但是,不好意思,Unity不能完美支持,比如在WebGL里面,就不支持多线程,async和await背后是多线程。所以,此路不通。

解决办法3:UniRX和UniTask
UniRX是上世代的产物,响应式编程兴起的时候创建的项目,至今已经进入稳态,官网特别说明,如果你要搞异步编程,那么就用UniTask,因为异步功能已经从UniRX拆分到UniTask了。

用UniTask完成以上业务:
点击【Button】后,启动发动机…停止发动机:
Unity中的异步编程【1】—— Unity与async 、 await_第3张图片
代码清单:

using UnityEngine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;//关键包
public class mytest : MonoBehaviour
{
    public Button myBtn;

    // Start is called before the first frame update
    void Start()
    {

        myBtn.onClick.AddListener(async ()=> 
        {
            //隔三秒启动发动机,发动机转4秒,然后停机
            Debug.Log($"{Time.realtimeSinceStartup}: 预热三秒");
            await UniTask.Delay(System.TimeSpan.FromSeconds(3),ignoreTimeScale:false);
            Debug.Log($"{Time.realtimeSinceStartup}: 启动发动机,发动机开始运转");
            await UniTask.Delay(System.TimeSpan.FromSeconds(4), ignoreTimeScale: false);
            Debug.Log($"{Time.realtimeSinceStartup}: 发动机停机");
        });
    }
}

四、发布和测试:WebGL发布并测试ok

Unity中的异步编程【1】—— Unity与async 、 await_第4张图片

五、附录

1、C#异步编程
2、UniRX —— Unity响应式编程
3、UniTask—— Unity异步编程

你可能感兴趣的:(unity,c#,游戏引擎)