zoukankan      html  css  js  c++  java
  • unity使用async await异步unitywebrequest 加载 streamingAssets文件,取代 WWW 和 协程

    如果现在你在中文网上查询一些关于加载 streamingAssetsPath 或者是 persistentDataPath 路径下的文件,你能找到的大部分都是让你用 WWW 配合 协程 来实现这一步。

    中文网上此类相关介绍很少。WWW 和 协程 的编写让代码变得臃肿,而协程连返回值都没。要么用大段代码块,或者是注入委托调用。

    unity现在已经支持 .net core 了,并且引入UnityWebRequest

    先上代码,加载本地的图片文件

    
    
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.CompilerServices;
    using System.Threading.Tasks;
    using UnityEngine;
    using UnityEngine.Networking;

    public static class LoadHelper
    {
    public static async Task<Sprite> LoadSpritePNG(string path)
    {
    var task = LoadAsyncSprite(path, ".png");
    Task<Sprite> t = await Task.WhenAny(task);
    return t.Result;
    }

    public static async Task<Sprite> LoadSpriteJPG(string path)
    {
    var task = LoadAsyncSprite(path, ".jpg");
    Task<Sprite> t = await Task.WhenAny(task);
    return t.Result;
    }

    static async Task<Sprite> LoadAsyncSprite(string url, string end)
    {
    string path = Path.Combine(Application.streamingAssetsPath, url + end);
    var getRequest = UnityWebRequest.Get(path);
    await getRequest.SendWebRequest();
    byte[] imgData = getRequest.downloadHandler.data;
    Texture2D tex = new Texture2D(2, 2);
    tex.LoadImage(imgData);
    Vector2 pivot = new Vector2(0.5f, 0.5f);
    Sprite sprite = Sprite.Create(tex, new Rect(0.0f, 0.0f, tex.width, tex.height), pivot, 100.0f);
    return sprite;
    }

    }

    public static class ExtensionMethods
    {
    public static TaskAwaiter GetAwaiter(this AsyncOperation asyncOp)
    {
    var tcs = new TaskCompletionSource<object>();
    asyncOp.completed += obj => { tcs.SetResult(null); };
    return ((Task)tcs.Task).GetAwaiter();
    }
    }
     

    调用即可

    ---------------------------------------------------------------------------------------分割--------------------------------------------------------------------------------------------------------

    好的那么下面来讲下async await

    让我们看一个简单的例子。给定以下协程:

    public class AsyncExample : MonoBehaviour
    {
        IEnumerator Start()
        {
            Debug.Log("Waiting 1 second...");
            yield return new WaitForSeconds(1.0f);
            Debug.Log("Done!");
        }
    }

    使用async-await的等效方法如下:

    public class AsyncExample : MonoBehaviour
    {
        async void Start()
        {
            Debug.Log("Waiting 1 second...");
            await Task.Delay(TimeSpan.FromSeconds(1));
            Debug.Log("Done!");
        }
    }

    简而言之,Unity协程使用C#对迭代器块的内置支持来实现您提供给StartCoroutine方法的IEnumerator迭代器对象由Unity保存,并且该迭代器对象的每一帧都向前推进以获取由协程产生的新值。然后,Unity会读取您“ yield return”的不同值以触发特殊情况的行为,例如执行嵌套协程(返回另一个IEnumerator时),延迟几秒钟(返回WaitForSeconds类型的实例时)或等到下一帧(返回null时)。

    不幸的是,由于async-await在Unity中是相当新的事实,因此上述对协程的内置支持并不像async-await那样以类似的方式存在。这意味着我们必须自己添加很多这种支持。

    但是,Unity确实为我们提供了一个重要方面。如您在上面的示例中看到的,默认情况下,我们的异步方法将在主unity线程上运行。在非统一C#应用程序中,异步方法通常会自动在单独的线程上运行,这在Unity中会是一个大问题,因为在这种情况下我们将无法始终与Unity API进行交互。没有Unity引擎的支持,我们对异步方法内部的Unity方法/对象的调用有时会失败,因为它们将在单独的线程上执行。在后台,它可以这样工作,因为Unity提供了一个默认的SynchronizationContext,称为UnitySynchronizationContext,它自动收集在每个帧中排队的所有异步代码,并继续在主要unity线程上运行它们。

    事实证明,这足以让我们开始使用async-await!我们只需要一些帮助程序代码就可以使我们做一些有趣的事情,而不仅仅是简单的时间延迟。

    当前,我们没有很多有趣的异步代码可以编写。我们可以调用其他异步方法,并且可以使用Task.Delay,就像上面的示例中一样,但除此之外就不多了。

    作为一个简单的示例,让我们添加直接在TimeSpan上“等待”的功能,而不必总是像上面的示例那样每次都调用Task.Delay。像这样:

    public class AsyncExample : MonoBehaviour
    {
        async void Start()
        {
            await TimeSpan.FromSeconds(1);
        }
    }

    我们需要做的就是为此添加一个自定义的GetAwaiter扩展方法到TimeSpan类中:

    public static class AwaitExtensions
    {
        public static TaskAwaiter GetAwaiter(this TimeSpan timeSpan)
        {
            return Task.Delay(timeSpan).GetAwaiter();
        }
    }

    之所以可行,是因为为了在更新版本的C#中支持“等待”给定的对象,所需要做的就是该对象具有一个名为GetAwaiter的方法,该方法返回一个Awaiter对象。这很棒,因为它允许我们通过使用上述扩展方法来等待我们想要的任何东西,而无需更改实际的TimeSpan类。

    我们也可以使用这种方法来支持等待其他类型的对象,包括Unity用于协程指令的所有类!我们可以使WaitForSeconds,WaitForFixedUpdate,WWW等全部等待,就像它们在协程中可屈服一样。我们还可以向IEnumerator添加GetAwaiter方法以支持等待协程,以允许将异步代码与旧的IEnumerator代码互换。

    ------------------------------------------------------------------------------------------------------------------------------

    此处已经有一些工具类,定义好了一些GetAwaiter

    https://github.com/svermeulen/Unity3dAsyncAwaitUtil/releases

    【使用】

    public class AsyncExample : MonoBehaviour
    {
        public async void Start()
        {
            // Wait one second
            await new WaitForSeconds(1.0f);
     
            // Wait for IEnumerator to complete
            await CustomCoroutineAsync();
     
            await LoadModelAsync();
     
            // You can also get the final yielded value from the coroutine
            var value = (string)(await CustomCoroutineWithReturnValue());
            // value is equal to "asdf" here
     
            // Open notepad and wait for the user to exit
            var returnCode = await Process.Start("notepad.exe");
     
            // Load another scene and wait for it to finish loading
            await SceneManager.LoadSceneAsync("scene2");
        }
     
        async Task LoadModelAsync()
        {
            var assetBundle = await GetAssetBundle("www.my-server.com/myfile");
            var prefab = await assetBundle.LoadAssetAsync<GameObject>("myasset");
            GameObject.Instantiate(prefab);
            assetBundle.Unload(false);
        }
     
        async Task<AssetBundle> GetAssetBundle(string url)
        {
            return (await new WWW(url)).assetBundle
        }
     
        IEnumerator CustomCoroutineAsync()
        {
            yield return new WaitForSeconds(1.0f);
        }
     
        IEnumerator CustomCoroutineWithReturnValue()
        {
            yield return new WaitForSeconds(1.0f);
            yield return "asdf";
        }
    }

    如您所见,像这样使用async await可能非常强大,尤其是当您像上面的LoadModelAsync方法中一样开始组合多个异步方法时。

    请注意,对于返回值的异步方法,我们使用Task的通用版本,并将返回类型作为通用参数传递,就像上面的GetAssetBundle一样。

    还要注意,在大多数情况下,使用上面的WaitForSeconds实际上比我们的TimeSpan扩展方法更可取,因为WaitForSeconds将使用Unity游戏时间,而我们的TimeSpan扩展方法将始终使用实时(因此不受Time.timeScale更改的影响)

    (摘自:http://www.stevevermeulen.com/index.php/blog/)

     视频地址  https://gametorrahod.com/unity-and-async-await/

  • 相关阅读:
    Spring面试,IoC和AOP的理解
    WEB打印(jsp版)
    Spring事务管理机制的实现原理-动态代理
    spring面试题
    oracle PLSQL基础学习
    oracle创建表空间
    WM_CONCAT字符超过4000的处理办法
    Oracle 数据泵使用详解
    Oracle 数据泵详解
    linux下启动oracle
  • 原文地址:https://www.cnblogs.com/sanyejun/p/14059723.html
Copyright © 2011-2022 走看看