How to load an image from URL with Unity?
Asked Answered
J

6

21

Please save me from going crazy.

No matter how many times I google, I always end up with (usually deprecated) versions of the following code:

IEnumerator setImage(string url) {
    Texture2D texture = profileImage.canvasRenderer.GetMaterial().mainTexture as Texture2D;

    WWW www = new WWW(url);
    yield return www;

    Debug.Log("Why on earh is this never called?");

    www.LoadImageIntoTexture(texture);
    www.Dispose();
    www = null;
}

I'm using Unity 5 not 4. The URL I'm trying to load exists. Please shine some light on me.

How do I load an image over HTTP and display it in a UnityEngine.UI.Image?

Japanese answered 1/8, 2015 at 19:43 Comment(2)
And how are you calling your setImage() method? any code you may want to share?Vinous
Pretty unspectacular... setImage(url); where url is an URL to a JPEG or PNG.Japanese
N
11

quick clarification: https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html

public void yourMethod()
{
   StartCoroutine(setImage("http://url/image.jpg")); //balanced parens CAS
}

IEnumerator setImage(string url) {
    Texture2D texture = profileImage.canvasRenderer.GetMaterial().mainTexture as Texture2D;

    WWW www = new WWW(url);
    yield return www;

    // calling this function with StartCoroutine solves the problem
    Debug.Log("Why on earh is this never called?");

    www.LoadImageIntoTexture(texture);
    www.Dispose();
    www = null;
}
Nagaland answered 25/7, 2016 at 7:4 Comment(1)
WWW is obsolete now. UnityWebRequestTexture is more preferable.Oxen
I
46

For Unity 2018+ use UnityWebRequest which replaces the WWW class.

void Start(){    
    StartCoroutine(DownloadImage(url));
}

IEnumerator DownloadImage(string MediaUrl)
{   
    UnityWebRequest request = UnityWebRequestTexture.GetTexture(MediaUrl);
    yield return request.SendWebRequest();
    if(request.isNetworkError || request.isHttpError) 
        Debug.Log(request.error);
    else
        YourRawImage.texture = ((DownloadHandlerTexture) request.downloadHandler).texture;
} 
Impassible answered 13/12, 2018 at 16:29 Comment(0)
U
26

You can do that with async/await:

using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;

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

        // await until it's done: 
        while( asyncOp.isDone==false )
            await Task.Delay( 1000/30 );//30 hertz
        
        // read results:
        if( www.isNetworkError || www.isHttpError )
        // 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:
            return DownloadHandlerTexture.GetContent(www);
        }
    }
}

Usage example:

[SerializeField] string _imageUrl;
[SerializeField] Material _material;
Texture2D _texture;

async void Start ()
{
    _texture = await GetRemoteTexture(_imageUrl);
    _material.mainTexture = _texture;
}

void OnDestroy () => Dispose();
public void Dispose () => Object.Destroy(_texture);// memory released, leak otherwise
Ul answered 13/12, 2018 at 22:14 Comment(3)
This is a cool way. Can we use this to load an image from local disk?Haslet
@karsnen Yes. Use valid file uri.Lutist
Highly recommend async operations if you're looking to get away from MonobehavioursSanyu
N
11

quick clarification: https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html

public void yourMethod()
{
   StartCoroutine(setImage("http://url/image.jpg")); //balanced parens CAS
}

IEnumerator setImage(string url) {
    Texture2D texture = profileImage.canvasRenderer.GetMaterial().mainTexture as Texture2D;

    WWW www = new WWW(url);
    yield return www;

    // calling this function with StartCoroutine solves the problem
    Debug.Log("Why on earh is this never called?");

    www.LoadImageIntoTexture(texture);
    www.Dispose();
    www = null;
}
Nagaland answered 25/7, 2016 at 7:4 Comment(1)
WWW is obsolete now. UnityWebRequestTexture is more preferable.Oxen
T
7

We can't directly apply texture material on the Image component in the canvas. So, we should create a sprite from the texture downloaded at runtime then apply that sprite in the Image component.

Try this one,

IEnumerator DownloadImage(string MediaUrl)
    {   
        UnityWebRequest request = UnityWebRequestTexture.GetTexture(MediaUrl);
        yield return request.SendWebRequest();
        if(request.isNetworkError || request.isHttpError) 
            Debug.Log(request.error);
        else{
            // ImageComponent.texture = ((DownloadHandlerTexture) request.downloadHandler).texture;

            Texture2D tex = ((DownloadHandlerTexture) request.downloadHandler).texture;
            Sprite sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(tex.width / 2, tex.height / 2));
            ImageComponent.GetComponent<Image>().overrideSprite = sprite;
        }
    } 
Tahiti answered 13/10, 2020 at 12:58 Comment(0)
J
1

Thanks to the question asked by Umair M, I figured out that this method needs to be called using unity's StartCoroutine.

Japanese answered 5/8, 2015 at 18:4 Comment(0)
S
1
public void OnStart()
{
    StartCoroutine(setImage("http://drive.google.com/myimage.jpg")); 
}

IEnumerator setImage(string url) 
{
    Texture2D texture = null;
    WWW www = new WWW(url);
    yield return www;

    Debug.Log("Why on earth is this never called?");
    texture = www.texture;
    //end show Image in texture 2D
}
Selfindulgent answered 13/6, 2019 at 16:23 Comment(1)
WWW class is deprecated and should not be used anymore. Use UnityWebRequest instead.Oreopithecus

© 2022 - 2024 — McMap. All rights reserved.