Reputation: 11
I'm pretty new to writing asynchronous stuff in Unity. It is pretty easy to create a progress bar for asynchronous operation, but when it comes to doing it for the asynchronous task I'm not even sure when to start :(
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;
}
While the photo from android is being add as a texture I want to create a progress bar. Thank you in advance for any suggestions
Upvotes: 1
Views: 2287
Reputation: 25
Today I would suggest you to use UniTask library. It allows to use Coroutines as async methods and vice versa
Then you could write something like this:
// 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);
Upvotes: 0
Reputation: 90872
I can see no reason in your code why you should be using async
at all. The only thing you actually are waiting for is the UnityWebRequest
. async
hasn't really any advantage over a simple Coroutine(IEnumerator
) here.
In addition async
stuff always also brings the problem of multi-threading ... Unity isn't thread safe so most of the API can only be used in the Unity mainthread.
You should rather stick to a Coroutine like e.g.
// 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;
}
Alternatively often recommended is a kind of "dispatcher" or I prefer to call it a main thread worker where you can pass actions from other threads back to the main thread like e.g.
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;
}
}
A cool way to have a simple progress bar for example can be achieved by using an UI.Image
with
Image Type
= FilledFill Method
= HorizontalFill Origin
= Leftnow all you have to do is update the image.fillAmount
Upvotes: 2