【问题标题】:Looking for a way to add a progress bar for asynchronic task (Unity, C#)寻找一种为异步任务添加进度条的方法(Unity,C#)
【发布时间】:2019-08-27 01:29:41
【问题描述】:

我对在 Unity 中编写异步内容非常陌生。为异步操作创建进度条非常容易,但是在为异步任务创建进度条时,我什至不确定何时开始:(

public async void GetPhotoFromAndroidAndAddTexture(RawImage mainImage, string path)
{
    await AddTextureToImage(mainImage, path);
}


public async Task<Texture2D> GetTextureFromAndroid(string path)
{
    if(path != "" && path != " ")
    {
        #if UNITY_ANDROID
            path = "file://" + path;
        #endif

        UnityWebRequest www = UnityWebRequestTexture.GetTexture(path);

        var asyncOperation = www.SendWebRequest();

        while(!asyncOperation.isDone)
        {
            await Task.Delay( 1000/30 );
        }

        return DownloadHandlerTexture.GetContent(www);
    }
    else
    {
        Debug.Log("The path is not correct!");
        return null;
    }
}

public async Task AddTextureToImage(RawImage mainImage, string path)
{
    IProgress<int> progress;
    Texture2D photoFromDevice = await GetTextureFromAndroid(path);

    float textureH = photoFromDevice.height;
    float textureW = photoFromDevice.width;

    float aspectRat = textureH > textureW ? textureH/textureW : textureW/textureH;

    mainImage.GetComponent<AspectRatioFitter>().aspectRatio = aspectRat;
    mainImage.texture = photoFromDevice;
}

当来自 android 的照片被添加为纹理时,我想创建一个进度条。 提前感谢您的任何建议

【问题讨论】:

  • async 总是有多线程的问题......你为什么不使用协程(IEnumerator),就像通常在examples 中显示的那样?

标签: unity3d asynchronous task progress-bar


【解决方案1】:

我在您的代码中看不出为什么您应该使用async。您真正在等待的是UnityWebRequest。 async 与这里的简单 Coroutine(IEnumerator) 相比并没有任何优势。

另外async的东西总是会带来多线程的问题……Unity不是线程安全的,所以大部分API只能在Unity主线程中使用。


您应该坚持使用Coroutine,例如

// wherever you want to show/use the progress
public float progress;

public void GetPhotoFromAndroidAndAddTexture(RawImage mainImage, string path)
{
    StartCoroutine(AddTextureToImage(mainImage, path));
}

public IEnumerator AddTextureToImage(RawImage mainImage, string path)
{
    Texture2D photoFromDevice;

    if (path != "" && path != " ")
    {
#if UNITY_ANDROID
        path = "file://" + path;
#endif

        UnityWebRequest www = UnityWebRequestTexture.GetTexture(path);

        var asyncOperation = www.SendWebRequest();

        while (!asyncOperation.isDone)
        {
            // wherever you want to show the progress:
            progress = www.downloadProgress;

            yield return null;
            // or if you want to stick doing it in certain intervals
            //yield return new WaitForSeconds(0.5f);
        }

        // you should also check for errors
        if(www.error != null) 
        {
            Debug.LogError("Something went wrong loading!", this);
            yield break;
        }

        photoFromDevice = DownloadHandlerTexture.GetContent(www);
    }
    else
    {
        Debug.LogError("The path is not correct!", this);
        yield break;
    }

    float textureH = photoFromDevice.height;
    float textureW = photoFromDevice.width;

    float aspectRat = textureH > textureW ? textureH / textureW : textureW / textureH;

    mainImage.GetComponent<AspectRatioFitter>().aspectRatio = aspectRat;
    mainImage.texture = photoFromDevice;
}

通常推荐的是一种“调度程序”,或者我更喜欢将其称为主线程工作者,您可以在其中将其他线程的操作传递回主线程,例如

private ConcurrentQueue<Action> callbacks = new ConcurrentQueue<Action>();

private void Update()
{
    while(callbacks.TryDequeue(var out a))
    {
        // Invokes the action in the main thread
        a?.Invoke();
    }
}

// example method to call
private void UpdateProgressBar(float progress)
{
    // use progress
}

public async Task<Texture2D> GetTextureFromAndroid(string path)
{
    if(path != "" && path != " ")
    {
        #if UNITY_ANDROID
            path = "file://" + path;
        #endif

        UnityWebRequest www = UnityWebRequestTexture.GetTexture(path);

        var asyncOperation = www.SendWebRequest();

        while(!asyncOperation.isDone)
        {
            // add a callback for the main thread using a lambda expression
            callbacks.Add(()=> { UpdateProgressBar(www.downloadProgress); });
            await Task.Delay( 1000/30 );
        }

        return DownloadHandlerTexture.GetContent(www);
    }
    else
    {
        Debug.Log("The path is not correct!");
        return null;
    }
}

使用UI.Image 和

  • Image Type = 已填满
  • Fill Method = 水平
  • Fill Origin = 左

现在您所要做的就是更新image.fillAmount

【讨论】:

  • 是的,我认为这是一个非常好的建议,我会将这个函数重写为协程。我仍然很好奇如何获得异步任务的进度以使用它,例如通过 Firebase 调用。
  • 你可能会使用例如GetFileAsync 并传入一个处理程序方法,您可以将 dispatch 返回主线程
  • async hasn't really any advantage over async 我想你的意思是IEnumerator 或Coroutine 那里。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多