Unity中的异步编程——在Unity中使用 C#原生的异步(Task,await,async) - System.Threading.Tasks

Posted dzj2021

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity中的异步编程——在Unity中使用 C#原生的异步(Task,await,async) - System.Threading.Tasks相关的知识,希望对你有一定的参考价值。

一、UniTask(Cysharp.Threading.Tasks)和Task(System.Threading.Tasks)的区别

  • 1、System.Threading.Tasks中的Task是.Net原生的异步和多线程包。
  • 2、UniTask(Cysharp.Threading.Tasks)是仿照.Net原生的Task,await,async开发的一个包,该包专门服务于Unity,所以取名UnityTask,简称UniTask。
  • 3、既然有Task了,为啥还要搞一个UniTask
  • (1)Task可以用在PC和android上,但是在WebGL上则会报错(与多线程的支持有关),你可以退而求其次,使用协程实现异步等待。但是这样容易形成两套代码,增加工作量。
  • (2)UniTask则是用一套代码打天下【一套写法,适用于PC,Android,WebGL等等】,UniTask用协程把await,async统一到多个运行平台。

背景:之前用Unity2020 + UniTask开发的项目,结果用户的需求是要集成到Unity2017.4.37的一个工程文件里面,所有只能返祖了。

二、为Task增加新的功能

  • 1、UniTask有WaitUntil(),而Task没有,只能手动添加一个
/// <summary>
/// 方法:WaitUntil
/// </summary>
/// <param name="predicate">条件语句</param>
/// <param name="sleep">delay事件</param>
/// <returns>Task</returns>
public static async Task WaitUntil(Func<bool> predicate, int sleep = 50)

    while (!predicate())
    
        await Task.Delay(sleep);            
    


  • 2、UniTask有DelayFrame(),而Task没有,手动添加一个
 /// <summary>
 /// 方法:等待X帧
 /// </summary>
 /// <returns>Task</returns>
 public static async Task DelayFrame(int count)
 
     var current = Time.frameCount;
     while (Time.frameCount - current > count)
     
         await Task.Yield();
     
 
  • 3、UniTask中为多种UI实现了异步点击的方法,在Task模式下,如何为一个Button打造一个异步点击方法——OnClickAsync
  • (1)使用扩展方法来实现
  • (2)给一个button的OnClick绑定一个方法,发生点击事件时,更改一个bool标记
  • (3)用Task的await来循环等待用于标记Button点击的bool标记是否为true
  • (4)取消button上OnClick中之前绑定的方法
  • (5)返回
 /// <summary>
    /// 为Button定制一个扩展方法:点击事件的异步等待
    /// </summary>
    /// <param name="button">按钮Button</param>
    /// <param name="delayMs">循环等待中delay的时间-毫秒</param>
    /// <returns>Task</returns>
    public static async Task OnClickAsync(this Button button,int delayMs = 50)
    
        bool clicked = false;
        UnityAction ClickAction = () => 
        
            clicked = true;
            //Debug.Log($"ClickAction() was called Time.realtimeSinceStartup");
        ;

        //Debug.Log("添加侦听");
        button.onClick.AddListener(ClickAction); //此处不能用兰姆达,不然传入的是临时定义的方法,而不是提前定义的。

        while (!clicked)
                    
            await Task.Delay(delayMs); //等待时间【ms毫秒】
        

        //Debug.Log("移除侦听");
        button.onClick.RemoveListener(ClickAction);

        ClickAction = null;
    

三、代码清单

代码【1】——扩展Task

using System;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

/// <summary>
/// Task扩展工具     【System.Threading.Tasks 】
///     not UniTask  【Cysharp.Threading.Tasks】
/// </summary>
public static class TaskUtils

    /// <summary>
    /// 方法:WaitUntil
    /// </summary>
    /// <param name="predicate">条件语句</param>
    /// <param name="sleep">delay事件</param>
    /// <returns>Task</returns>
    public static async Task WaitUntil(Func<bool> predicate, int sleep = 50)
    
        while (!predicate())
        
            await Task.Delay(sleep);            
        
    

    /// <summary>
    /// 方法:等待X帧
    /// </summary>
    /// <returns>Task</returns>
    public static async Task WaitFrame(int count)
    
        var current = Time.frameCount;
        while (Time.frameCount - current > count)
        
            await Task.Yield();
        
    

    /// <summary>
    /// 方法:等待X帧
    /// </summary>
    /// <returns>Task</returns>
    public static async Task DelayFrame(int count)
    
        var current = Time.frameCount;
        while (Time.frameCount - current > count)
        
            await Task.Yield();
        
        

【2】——测试Task

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Threading.Tasks;
using System;

/// <summary>
/// 按钮被点击的事件绑定
/// </summary>
public class ButtonClicked : MonoBehaviour 

	public Button myButton;

	// Use this for initialization
	void Start () 

		Func<Task> Flow = async() => 
		
            while (true)
            
				Debug.Log($"等待按钮被点击-Time.realtimeSinceStartup");
				await myButton.OnClickAsync();
				Debug.Log($"按钮点击完成-Time.realtimeSinceStartup");
						
		;
		Flow();
	

  • 【3】测试结果
    测试环境Unity2017.4.37 Editor + Win10

我不能将脚本分配给 UNITY 中的按钮?

【中文标题】我不能将脚本分配给 UNITY 中的按钮?【英文标题】:I can't assign scripts to buttons in UNITY? 【发布时间】:2019-05-11 04:22:56 【问题描述】:

我刚开始探索 Unity,我正在使用 UNITY 2D 制作游戏。 我决定从一个简单的菜单屏幕开始习惯 UNITY。 我制作了菜单并继续对按钮进行编程。

但是,当我将脚本(或持有脚本的游戏对象)拖入单击部分的按钮并尝试选择我的功能时,它不会显示。

我的统一窗口

点击脚本按钮中的经理。

我的 C# 代码(这是我第一次使用 C#)

我想知道的是:我做错了什么吗?统一的工作方式与我的理解不同吗?团结在做一些奇怪的事情吗?我该如何解决。

(对于奇怪的帖子布局我不知道为什么这样做)。

【问题讨论】:

Alex - 在第 9 分钟左右结帐this tutorial。 投票关闭是一个印刷错误,因为缺少 ; 已阻止 Unity 编译新脚本,以便可以在检查器中访问它。 【参考方案1】:

你需要:

    在 Buttons 类的行尾添加分号 将 Buttons 类添加到场景中的 _Manager 游戏对象

【讨论】:

如果脚本在_Manager GameObject上,应该没有问题。【参考方案2】:

只需在代码末尾插入; 就可以了。您应该注意到编译器抱怨缺少分号。另外,请确保将您的脚本附加到_Manager GameObject。

【讨论】:

以上是关于Unity中的异步编程——在Unity中使用 C#原生的异步(Task,await,async) - System.Threading.Tasks的主要内容,如果未能解决你的问题,请参考以下文章

C#Unity3D中的C#编程初级

Unity 在使用 Firebase 数据库进行异步编程时崩溃

[Unity] C#中级编程 - 09 - 扩展方法

[Unity] C#中级编程 - 02 - 静态

C# EF6 使用 Unity 对一个上下文进行多次异步调用 - Asp.Net Web Api

[Unity] C#中级编程 - 05 - 构造函数/封装/继承