1

I am writing a Pokemon TCG application, and I need to obtain card images at runtime from URLs (such as https://images.pokemontcg.io/bw1/1.png) in order to use them in sprite renderers. This must be done at runtime, because I won't know in advance which card images I will need.

To test my functions I used the url and a legacy button calling the LoadSprite funtion and a SpriteRenderer. But, when calling following function, the unity editor froze completely.

    [SerializeField] private SpriteRenderer card;
    [SerializeField] private string url;
 public void LoadSprite()
    {
        GenerateCard setCardSprite = new GenerateCard();
        setCardSprite.SetImage(card,url);
    }

The GenerateCard class (unfinished):

public class GenerateCard
{
    public void SetImage(SpriteRenderer card, string url)
    {
        Task<Texture2D> textureTask = DownloadImages.GetRemoteTexture(url);

        Texture2D cardimage = textureTask.Result;

        Rect rec = new Rect(0, 0, cardimage.width, cardimage.height);

        Sprite.Create(cardimage, rec, new Vector2(0, 0), 1);

        card.sprite = Sprite.Create(cardimage, rec, new Vector2(0, 0), .01f);
    }
}

I got the DownloadImages class with the GetRemoteTexture function from this StackOverflow question.

I tried using Debug.Log to find out which line causes the freeze, but nothing gets sent to the console. I also replaced (request.isNetworkError || request.isHttpError) with the newer (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError):

public class DownloadImages
{

    public static async Task<Texture2D> GetRemoteTexture(string url)
    {
        Debug.Log("function was called");
        using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url))
        {
            // begin request:
            Debug.Log("begins request");
            var asyncOp = www.SendWebRequest();

            // await until it's done: 
            Debug.Log("start waiting");
            while (asyncOp.isDone == false)
                await Task.Delay(1000 / 30);//30 hertz
            Debug.Log("finished waiting");
            // read results:
            if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
            // if( www.result!=UnityWebRequest.Result.Success )// for Unity >= 2020.1
            {
                // log error:
                #if DEBUG
                Debug.Log($"{www.error}, URL:{www.url}");
                #endif

                // nothing to return on error:
                return null;
            }
            else
            {
                // return valid results:
                Debug.Log("Texture downloaded");
                return DownloadHandlerTexture.GetContent(www);
            }
        }
    }
}

I hope somebody knows why unity freezes and Thank You

  • 1
    im unsure which unity version you are using but usually a coroutine is the documented way to use the unitywebrequest class, I am not sure it is designed to work reliably as expected here – BugFinder Aug 29 '23 at 21:23
  • I use Unity version 2023.10f1 Thank you for the tipp, I will take a look at the docs – Gurkenschnitzel Aug 29 '23 at 22:01
  • 2
    You should check out [UniTask](https://github.com/Cysharp/UniTask), which is an asynchronous library made for unity. – hijinxbassist Aug 29 '23 at 22:20
  • @hijinxbassist but wouldn't that mean I should get it after the image is downloaded? or am i missing something? I let the program run for about 30 minutes and it didn't complete. – Gurkenschnitzel Aug 29 '23 at 23:08
  • @BugFinder Thanks again for the tip, it works with a coroutine. I'll put the new Code into the Question. – Gurkenschnitzel Aug 29 '23 at 23:10
  • I thought it was frozen only while downloading, ie blocked thread, my mistake\. – hijinxbassist Aug 29 '23 at 23:18
  • @Gurkenschnitzel I'd suggest removing your solution from the question, and instead putting that solution into an answer to this question (and then accepting that answer). This will indicate to everyone else that your question has now been answered (even if you answered it yourself) – 11belowstudio Aug 30 '23 at 12:31
  • Th original issue might be that one of the things you are awaiting needs to be on the Unity main thread (some of its API is not thread-safe) .. it might just silently throw an exception and never finish – derHugo Aug 30 '23 at 14:06
  • @11belowstudio like this? I am pretty new to stack overflow so if it's missing something it would be nice if you can tell me – Gurkenschnitzel Aug 31 '23 at 08:49
  • yep, you did it correctly :) – 11belowstudio Aug 31 '23 at 09:40

1 Answers1

2

I got it working by using a Coroutine. The Sprite is very big and needs to be scaled down but it gets downloaded correctly.

public IEnumerator DownloadImage(string MediaUrl, SpriteRenderer card)
    {
        Texture2D cardimage = null;
        UnityWebRequest request = UnityWebRequestTexture.GetTexture(MediaUrl);
        yield return request.SendWebRequest();
        if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
            Debug.Log(request.error);
        else
            cardimage = ((DownloadHandlerTexture)request.downloadHandler).texture;
        if (cardimage == null)
        {
            Debug.Log("cardimage is null");
        }
        Rect rec = new Rect(0, 0, cardimage.width, cardimage.height);
        Sprite sprite = Sprite.Create(cardimage, rec, new Vector2(0, 0), 1);
        card.sprite = sprite;
    }