unity3d 正在寻找一种为异步任务添加进度条的方法(Unity,C#)

f3temu5u  于 2023-04-21  发布在  C#
关注(0)|答案(2)|浏览(201)

我对在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的照片是作为一个纹理添加我想创建一个进度条。提前感谢您的任何建议

m1m5dgzv

m1m5dgzv1#

我看不出你的代码中有什么理由要使用async。你实际上在等待的是UnityWebRequestasync在这里并没有比简单的CoroutineIEnumerator)有任何优势。
此外,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

vngu2lb8

vngu2lb82#

今天我建议你使用UniTask library。它允许使用协程作为异步方法,反之亦然
你可以这样写:

// Also, you can use Progress.CreateOnlyValueChanged<float>(...)
var progress = Progress.Create<float>(x => {
     // Do something with progress...
     Debug.Log(x);
});

var request = await UnityWebRequestTexture.GetTexture(path)
    .SendWebRequest()
    .ToUniTask(progress: progress);
return DownloadHandlerTexture.GetContent(request);

相关问题