How to load an image from URL with Unity? - c#

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?

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;
}

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

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;
}

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;
}
}

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

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
}

Related

Unity C# - Is there a faster way to load a number of image files from disk than UnityWebRequest, WWW or File.ReadAllBytes?

Its a VN style game with user generated content and I need to load the image without delay.
Due to it being user generated content the images will be siting in a folder with the game.
Due to the same reason I cant preload the images, since I cant know which will be the next image to load.
I have tried UnityWebRequest, WWW or File.ReadAllBytes and all have more delay than I expected, even thou Im running it on a SSD.
Is there a faster way?
The code im using for testing the loading time of the images
using UnityEngine;
using System.IO;
using UnityEngine.Networking;
using System.Collections;
using UnityEngine.UI;
using System.Threading.Tasks;
/// <summary>
/// 2020/19/05 -- Unity 2019.3.3f1 -- C#
/// </summary>
public class itemCreatorImageLoad : MonoBehaviour
{
public Image image; // this is referencing to a UI Panel
private Texture2D texture2D;
private UnityWebRequest uwr;
public RawImage rawImage; // this is referencing to a UI rawImage
// path = #"C:\UnityTests\Referencing\Referencing\Assets\StreamingAssets\Items\image.png"
// the # handles the / and \ conventions that seem to come from the program using paths for the web
// C:/.../.../... web
// C:\...\...\... pc
public void LoadImageWWW(string path)
{
if (texture2D)
{
Destroy(texture2D); // this follows the reference and destroys the texture. Else it would just get a new one and the old textures start piling up in your memory, without you being able to remove them.
}
texture2D = new Texture2D(1, 1);
texture2D = new WWW(path).textureNonReadable as Texture2D;
image.sprite = Sprite.Create(texture2D, new Rect(0.0f, 0.0f, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f), 100.0f);
image.preserveAspect = true;
}
public void LoadImageWWWv2(string path)
{
// "http://url/image.jpg"
StartCoroutine(setImage(path));
}
IEnumerator setImage(string url) // this comes from https://stackoverflow.com/questions/31765518/how-to-load-an-image-from-url-with-unity
{
Texture2D texture = image.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;
}
public void LoadImageReadAllBytes(string path)
{
byte[] pngBytes = File.ReadAllBytes(path);
if (texture2D)
{
Destroy(texture2D); // this follows the reference and destroys the texture. Else it would just get a new one and the old textures start piling up in your memory, without you being able to remove them.
}
texture2D = new Texture2D(1, 1);
texture2D.LoadImage(pngBytes);
image.sprite = Sprite.Create(texture2D as Texture2D, new Rect(0.0f, 0.0f, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f), 100.0f);
image.preserveAspect = true;
}
public void LoadImageUnityWebRequest(string path)
{
StartCoroutine(LoadImageCorroutine());
IEnumerator LoadImageCorroutine()
{
using (uwr = UnityWebRequestTexture.GetTexture(#path))
{
yield return uwr.SendWebRequest();
// I would always check for errors first
if (uwr.isHttpError || uwr.isNetworkError)
{
Debug.LogError($"Could not load texture do to {uwr.responseCode} - \"{uwr.error}\"", this);
yield break;
}
// Destroy the current texture instance
if (rawImage.texture)
{
Destroy(texture2D); // this follows the reference and destroys the texture. Else it would just get a new one and the old textures start piling up in your memory, without you being able to remove them.
}
rawImage.texture = DownloadHandlerTexture.GetContent(uwr);
image.sprite = Sprite.Create(rawImage.texture as Texture2D, new Rect(0.0f, 0.0f, rawImage.texture.width, rawImage.texture.height), new Vector2(0.5f, 0.5f), 100.0f);
image.preserveAspect = true;
}
StopCoroutine(LoadImageCorroutine());
}
}
public void LoadImageUnityWebRequestv2(string path)
{
StartCoroutine(LoadImageUnityWebRequestv2Coroutine(path));
}
IEnumerator LoadImageUnityWebRequestv2Coroutine(string MediaUrl) // this comes from https://stackoverflow.com/questions/31765518/how-to-load-an-image-from-url-with-unity
{
UnityWebRequest request = UnityWebRequestTexture.GetTexture(MediaUrl);
yield return request.SendWebRequest();
if (request.isNetworkError || request.isHttpError)
{
Debug.Log(request.error);
}
else
{
rawImage.texture = ((DownloadHandlerTexture)request.downloadHandler).texture;
}
}
// a async version that I grabbed from somewhere, but I dont remember where anymore
[SerializeField] string _imageUrl;
[SerializeField] Material _material;
public async void MyFunction()
{
Texture2D texture = await GetRemoteTexture(_imageUrl);
_material.mainTexture = texture;
}
public static async Task<Texture2D> GetRemoteTexture(string url)
{
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url))
{
//begin requenst:
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)
{
//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);
}
}
}
}

Make Material From Texture2D

I'm trying to write a program in unity. I'm quite new to unity, so i don't know much.i want to be able to make a new material in my code with only a texture2d to pass into it. So far im trying this:
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class Search : MonoBehaviour
{
public void SearchEnter()
{
MyObject.GetComponent<Image>().material = LoadImage(name.ToLower());
}
Texture2D LoadImage(string ImageName)
{
string url = "file:///C:/MyFilePath" + ImageName + ".jpg";
Texture2D tex;
tex = new Texture2D(4, 4, TextureFormat.DXT1, false);
using (WWW www = new WWW(url))
{
www.LoadImageIntoTexture(tex);
GetComponent<Renderer>().material.mainTexture = tex;
}
return tex;
}
}
I cannot do this as it cannot convert from texture2d to material.
Can anyone help me?
First of all you are not waiting until the WWW request finishes so you are trying to access the result to early.
Instead of the WWW you should rather use a UnityWebRequestTexture.GetTexture in a Coroutine like
public class Search : MonoBehaviour
{
public void SearchEnter()
{
StartCoroutine(LoadImage(name.ToLower()))
}
IEnumerator LoadImage(string ImageName)
{
using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture("file:///C:/MyFilePath" + ImageName + ".jpg"))
{
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError)
{
Debug.Log(uwr.error);
}
else
{
// Get downloaded texture
var texture = DownloadHandlerTexture.GetContent(uwr);
//TODO now use it
...
}
}
}
}
The second issue: You can not simply convert a Texture2D to Material. You rather have to either create a new Material from a given shader or a given Material using new Material() e.g. using Shader.Find like
//TODO now use it
// ofcourse again you can not simply create a new material from a texture
// you would rather need to pass in a shader like
var material = new Material(Shader.Find("UI/Default"));
material.mainTexture = texture;
MyObject.GetComponent<Image>().material = material;
GetComponent<Renderer>().material.mainTexture = texture;
However, for an Image you would actually probably rather simply stick with the default UI material but use a Sprite instead
MyObject.GetComponent<Image>().sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.one * 0.5f);

Using Unity Editor, how do i upload a file from my computer and have it appear on a 3D object or plane?

I found a tutorial on YouTube that accurately added File Explorer and image upload to a 'RawImage' on a canvas using Unity 2017.3.1f1.
What I'm trying to do is add the same image after 'button press' to a 3D object like a cube or plane as shown by the colored cube. When I run the below code, it registers as being present on the cube but doesn't render. Any help is appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
public class Explorer : MonoBehaviour
{
string path;
public RawImage image;
public void OpenExplorer()
{
path = EditorUtility.OpenFilePanel("Overwrite with png", "", "png");
GetImage();
}
void GetImage()
{
if (path != null)
{
UpdateImage();
}
}
void UpdateImage()
{
WWW www = new WWW("file:///" + path);
image.texture = www.texture;
}
}
There is a tiny bug in your code. It should work sometimes and fail other times. The chances of it working or not depends on the size of the image. It will work if the image is really small but fail when it is a large image.
The reason for this is because of the code in your UpdateImage function. The WWW is supposed to be used in a coroutine function because you need to yield or wait for it to finish loading or downloading the file before accessing the texture with www.texture. Your are not doing this now. Change it to a coroutine function then yield it and it should work fine,.
void GetImage()
{
if (path != null)
{
StartCoroutine(UpdateImage());
}
}
IEnumerator UpdateImage()
{
WWW www = new WWW("file:///" + path);
yield return www;
image.texture = www.texture;
}
If some reason you can't use a coroutine because it's an Editor plugin then forget about the WWW API and use use File.ReadAllBytes to read the image.
void GetImage()
{
if (path != null)
{
UpdateImage();
}
}
void UpdateImage()
{
byte[] imgByte = File.ReadAllBytes(path);
Texture2D texture = new Texture2D(2, 2);
texture.LoadImage(imgByte);
image.texture = texture;
}
To assign the image to a 3D Object, get the MeshRenderer then set the texture to the mainTexture of the material the renderer is using:
//Drag the 3D Object here
public MeshRenderer mRenderer;
void UpdateImage()
{
byte[] imgByte = File.ReadAllBytes(path);
Texture2D texture = new Texture2D(2, 2);
texture.LoadImage(imgByte);
mRenderer.material.mainTexture = texture;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using System.IO;
public class Explorer : MonoBehaviour
{
string path;
public MeshRenderer mRenderer;
public void OpenExplorer()
{
path = EditorUtility.OpenFilePanel("Overwrite with png", "", "png");
GetImage();
}
void GetImage()
{
if (path != null)
{
UpdateImage();
}
}
void UpdateImage()
{
byte[] imgByte = File.ReadAllBytes(path);
Texture2D texture = new Texture2D (2, 2);
texture.LoadImage(imgByte);
mRenderer.material.mainTexture = texture;
//WWW www = new WWW("file:///" + path);
//yield return www;
//image.texture = texture;
}
}

WaitForSeconds Not Working[c#]

So I was making a game using the Unity 3d game engine, and I was using WaitForSeconds, but I keep running into various errors depending on what I change. Here is my code (without the WaitForSeconds):
using UnityEngine;
using System.Collections;
public class Attack : MonoBehaviour {
public int attack = 1;
public ParticleSystem MA;
// Use this for initialization
void Start () {
MA.enableEmission = false;
}
// Update is called once per frame
void Update () {
if(Input.GetKeyDown (KeyCode.E)){
attack++;
if(attack > 3){
attack = 1;
}
print (attack);
}
if(attack == 1){
if(Input.GetKeyDown (KeyCode.Q)){
print ("punch");
}
}
if(attack == 2){
if(Input.GetKeyDown (KeyCode.Q)){
/*if(Input.GetKeyDown (KeyCode.Q)){
WaitForSeconds magicdelay = new WaitForSeconds(1.0f);
yield return magicdelay;
/*MA.enableEmission = true;
yield return WaitForSeconds(1f);
MA.enableEmission = false;
}*/
print ("magic");
MA.enableEmission = true;
//I need help with the WaitForSeconds her
}
}
}
}
I tried YieldInstruction WaitForSeconds = (int)1f; multiple times, but that didn't work. Help please!
You have to use the Unity3d coroutines to solve the problem.
There are some useful links that can help:
Unity3D coroutine class
How to start Unity3D coroutines
if(Input.GetKeyDown (KeyCode.Q)){
StartCoroutine("Magic");
}
IEnumerator Magic(){
print ("magic");
MA.enableEmission = true;
yield return new WaitForSeconds(1.0f);
MA.enableEmission = false;
}
This should work:
void Start ()
{
StartCoroutine (MyCoroutine ());
}
IEnumerator MyCoroutine ()
{
while(true)
{
Debug.Log(Time.time);
yield return new WaitForSeconds(1f);
}
}

download png before loading scene

I develop Web Player application.
I need to download *.png image and use this image in scene.
Download code:
public Material mat;
string fullFilename;
Texture2D texTmp;
Sprite spr;
void Awake()
{
fullFilename = "http://585649.workwork.web.hostingtest.net/Images/Logo.png";
StartCoroutine(Download());
texTmp = new Texture2D(50, 50);
spr = Sprite.Create(texTmp, new Rect(0, 0, texTmp.width, texTmp.height), Vector2.zero, 100);
spr.texture.wrapMode = TextureWrapMode.Clamp;
mat.mainTexture = spr.texture;
}
IEnumerator Download()
{
WWW www = new WWW(fullFilename);
yield return www;
www.LoadImageIntoTexture(texTmp);
}
This work fine,but after loading scene uploaded picture appears after a while.
How i can fix it ?
Sorry for my English :)
Thanks!
This is natural. Because you download picture from internet, and there are some delays. So you add loading screen or wait all scene until picture is downloaded by you. But i think it is not good solution because you only load picture. Maybe disabling other buttons/interactive elements before starting download and then enable them after download is finished is good solution.
For example:
void Awake()
{
fullFilename = "http://585649.workwork.web.hostingtest.net/Images/Logo.png";
disableButtons();
StartCoroutine(Download());
texTmp = new Texture2D(50, 50);
spr = Sprite.Create(texTmp, new Rect(0, 0, texTmp.width, texTmp.height), Vector2.zero, 100);
spr.texture.wrapMode = TextureWrapMode.Clamp;
mat.mainTexture = spr.texture;
}
IEnumerator Download()
{
WWW www = new WWW(fullFilename);
yield return www;
www.LoadImageIntoTexture(texTmp);
enableButtons();
}

Categories

Resources