Piotr
Piotr

Reputation: 11

Problem with getting the random photo from Firebase by usage of async Tasks and async UnityWebRequest

I've created a call to Firebase to get the random photo (since we have categories of photos, first I'm trying to get random category, then random photo from it). After that I want to make async UnityWebRequest to get the photo and add it as a texture. The code gets to the inside of the Task but the call to database is never executed. I tried the code to get the image separately and it worked just fine. I also tried using delegate and action, but didn't help much. I'm still pretty newbie to C# and Unity, so my code isn't that good. Will appreciate all the feedback.

I tried the code to get the image separately and it worked just fine. I also tried using delegate and action, but didn't help much. I'm still pretty newbie to C# and Unity, so my code isn't that good. Will appreciate all the feedback.

//Getting the random photo

async Task GetRandomPhoto(){ await photosDbReference.GetValueAsync().ContinueWith(task =>{

        List<string> snapshotList = new List<string>();
        List<string> snapsnotList2 = new List<string>();

        if(task.IsCompleted){

            int catNumber = Convert.ToInt32(task.Result.ChildrenCount);

            System.Random rnd = new System.Random();
            int randCat = rnd.Next(0, catNumber);

            foreach (DataSnapshot snapshot in task.Result.Children)
            {
                snapshotList.Add(snapshot.Key.ToString());
            }

            photosDbReference.Child(snapshotList[randCat]).GetValueAsync().ContinueWith(task2 =>{

                if(task2.IsCompleted){
                    int photosNumber = Convert.ToInt32(task2.Result.ChildrenCount);
                    System.Random rnd2 = new System.Random();
                    int randPhoto = rnd.Next(0, photosNumber);

                    foreach(DataSnapshot snap2 in task2.Result.Children){
                        snapsnotList2.Add(snap2.Child("Dblink").Value.ToString());
                    }

                    string photoLink = snapsnotList2[randPhoto];
                }
            });
        }
    });

}

//Trying to set the photo as a texture public async void PutTheTexture(string url){

    Texture2D texture = await GetTexture(url);
    myImage.texture = texture;
}

public async Task<Texture2D> GetTexture(string url){
    Debug.Log("Started");
    UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);

    Debug.Log("Sending request: " + url);

    var asyncOp = www.SendWebRequest();

    Debug.Log("Request sent");

    while( asyncOp.isDone==false )
    {
        await Task.Delay( 1000/30 );
    }

    if( www.isNetworkError || www.isHttpError )
    {

        #if DEBUG
        Debug.Log( $"{ www.error }, URL:{ www.url }" );
        #endif
        return null;
    }
    else
    {
        return DownloadHandlerTexture.GetContent( www );
    }
}

The code gets to the Debug.Log("Started"); inside the Task but apparently the request is never send.

Upvotes: 0

Views: 412

Answers (2)

Piotr
Piotr

Reputation: 11

Big Thank You to everybody who tried to help! I finally found the way to solve the issue. I changed my async Task to async Task < "Dictionary" > and made it return the dict wilth all the data of the random photo (label, link, user). Then I created async void in which I wrote: Dictionary photoData = await GetRandomPhoto(); From there it was very easy.

Upvotes: 0

Patrick Martin
Patrick Martin

Reputation: 3131

I can't quite tell how your two blocks of code go together, but what I will point out is that .ContinueWith will not continue in Unity's main thread. My suspicion is that the continuation is kicking off the GetTexture via a mechanism I'm not seeing.

As far as I can tell, async/await should always stay in your current execution context but perhaps the Continuations are causing your logic to execute outside of the Unity main thread.

Since you're using Firebase, this would be super easy to test by replacing ContinueWith with the extension method ContinueWithOnMainThread. If this doesn't help, you can generally swap out async/await logic with continuations on tasks or fairly easily convert the above example to use purely coroutines:

//Getting the random photo
void GetRandomPhoto(){
    photosDbReference.GetValueAsync().ContinueWithOnMainThread(task =>
    {
        List<string> snapshotList = new List<string>();
        List<string> snapsnotList2 = new List<string>();

        if(task.IsCompleted){

            int catNumber = Convert.ToInt32(task.Result.ChildrenCount);

            System.Random rnd = new System.Random();
            int randCat = rnd.Next(0, catNumber);

            foreach (DataSnapshot snapshot in task.Result.Children)
            {
                snapshotList.Add(snapshot.Key.ToString());
            }

            photosDbReference.Child(snapshotList[randCat]).GetValueAsync().ContinueWithOnMainThread(task2 =>{

                if(task2.IsCompleted){
                    int photosNumber = Convert.ToInt32(task2.Result.ChildrenCount);
                    System.Random rnd2 = new System.Random();
                    int randPhoto = rnd.Next(0, photosNumber);

                    foreach(DataSnapshot snap2 in task2.Result.Children){
                        snapsnotList2.Add(snap2.Child("Dblink").Value.ToString());
                    }

                    string photoLink = snapsnotList2[randPhoto];
                }
            });
        }
    });
}

public delegate void GetTextureComplete(Texture2D texture);

private void Completion(Texture2D texture) {
    myImage.texture = texture;
}

//Trying to set the photo as a texture
public void PutTheTexture(string url){
    GetTexture(url, Completion);
}

public IEnumerator GetTexture(string url, GetTextureComplete completion){
    Debug.Log("Started");
    UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);

    Debug.Log("Sending request: " + url);

    var asyncOp = www.SendWebRequest();

    Debug.Log("Request sent");
    yield return asyncOp;

    if( www.isNetworkError || www.isHttpError )
    {

        #if DEBUG
        Debug.Log( $"{ www.error }, URL:{ www.url }" );
        #endif
        completion(null);
    }
    else
    {
        completion(DownloadHandlerTexture.GetContent(www));
    }
}

(you can do better than my example, and I haven't verified that it runs. Just a quick pass)

Upvotes: 2

Related Questions