Coroutines yield and monobehaviour

I trying to set something up that will download an image and apply it to a texture all in its own class but I can’t seem to get my head around the correct combination of yields and corountines

This is the code I have which does what I what, but I think in a totally “incorrect” way

using UnityEngine;
using System.Collections;
using System.IO;

public class urlImage {
	
	private bool _loaded = false;
	private Texture2D _image;
	private WWW _www;
	
	public urlImage(string url) {
		//check url is valid image
		//set texture to missing image if not
		string extension = Path.GetExtension(url);
		
		if (extension.ToLower() == ".jpg") {
			//begin download
			_www = new WWW (url);
			
			loadImage();

		}
	}
	
	IEnumerator loadImage () {
		yield return _www;
	}
	
	public void draw(float w) {
		if (_loaded) {
			GUILayout.Box(_image);
		}
		else {
			GUILayout.Label("Missing image");	
			if (_www.isDone) {
				_image = _www.texture;
				_loaded = true;
			}
		}
	}
	
}

So in this version the draw call is made every frame and I wait for the isDone to return true. However I think this should all be possible simply by waiting for the loadImage() function to return. However no matter what I try this is the only way I can get it to do things in the order I need.

What is the correct way to set this up?

Hi monark,

Your class must inherit from MonoBehaviour, and you must use StartCoroutine;

look at documentation exemple :

Julien G.

Yes I saw that and started down that route, but once I inherit from MonoBehaviour I can no longer create an instance of the class without an object.

So the following code fails to compile

_downloadedImages.Add(id, new urlImage(url));

Where _downloadedImages is hashtable list of images I’ve downloaded.

How do you get round that?

you can’t do that with coroutine but you can do same think like that :

public class example : MonoBehaviour {

    void LoadImg(string _path) {
        StartCoroutine(LoadImg(_path));
    }

    IEnumerator LoadImg(string _path) {
         WWW www = new WWW(_path);
         yield return www;

       // manage error case ...

       m_downloadedImages.Add(id, www.image);
     
    }
}

Julien G.

Use a singleton manager that facilitates the downloads and provide it a callback to call when its ready with the data sent through, thats the easiest way.

If you want to keep going with your ‘new’ approach bombing the environment, you will need to use AddComponent, thats the ‘constructor’ for anything that extends UnityEngine.Component

I don’t suppose there is any example code kicking around that implements this download manager idea, I’m not sure I can follow what you mean directly from that one liner. (edit: actually I think I see what you mean… I’ll give that a try)

In regards to the AddComponent approach I thought you needed a GameObject to call that, how would I replace new with that?

@JulienG The problem with that method is I then don’t have a list of classes just a whole bunch of images. I want to expand that urlImage class to do other things with the images.

MonoBehaviors don’t exist without game objects basically thats correct. As such you either have one or you need to rethink your structure. The unity engine is built around game objects and components, not around ‘singular classes in space’ ie System.Object direct extends.

As for the download manager: I’ve no code for this specific generic case as we had special needs but generally its simple, the code below was written from head and assumes that you don’t have a download manager prefab in the scene (if you do that replace the go creation and add component with findobjectoftype)

using UnityEngine;
public class DownloadManager : MonoBehaviour
{
  // the singleton
  static DownloadManager _instance;
  public static DownloadManager Instance {
    get{
      if (_instance == null)
      {
        GameObject go = new GameObject("DownloadManager");
        _instance = go.AddComponent<DownloadManager>();
      }
      return _instance;
    }
  }

  public delegate void DownloadTextureCallback(Texture2D texture);
  // implement your download coroutine and public download starter functions
  // that accept callback delegates here

  public void DownloadTextureFromURL (string url, DownloadTextureCallback callback)
  {
    StartCoroutine(Download(url,callback));
  }

  IEnumerator Download(string url, DownloadTextureCallback callback)
  {
    WWW request = new WWW(url);
    yield return request;
    if (request.error == null)
    {
      if (callback != null)
        callback(www.texture);
    }
  }
}

Usage would be through DownloadManager.Instance.DownloadTextureFromURL(“someurl”,myCallback);

public static DownloadManager Instance {

This line seems to be causing a problem, I can only get it to compile by changing it to “Instance()”

But then when i try and invoke it I get this error

sorry my error there when writing out of head and thinking 2 functions ahead already. its just a property hence its meant to be

public static DownloadManager Instance {
  get {
    // whats in the curly {}
  }
}

That’s got it, perfect thank you for all your help, I learned a ton of new stuff here in this one example.