Hey Guys,
While working on my first AssetStore submission (not that it’s in any way relevant to this post) I had a constant urge to download stuff stored locally and sometimes remotely.
Since I’m lazy and have recently developed an unhealthy attachment to Extension and Anonymous methods I wrote a wrapper to help me deal with my downloading needs. To complicate things I threw in a little bit of generics for good measure.
Anywho. First, add a fresh .cs file to your solution and drop this in :
#region >>>> Usings
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#endregion
/// <summary>
/// Some useful extension methods for all kinds of stuff.
/// </summary>
public static partial class UnityExtensions
{
#region >>>> WWW
/// <summary>
/// Use this extension method to download assets from a given URL.
/// It will attach a temporary wwwDowloader component to the target game object that will be destroyed once the download is complete.
/// </summary>
/// <typeparam name="T">
/// Supported Types :
///
/// AssetBundle - to download WWW.assetBundle
/// AudioClip - to download WWW.audioClip
/// Object - to download WWW.bytes (once the download is complete cast returned value to a byte array)
/// Texture - to download WWW.texture
/// MovieTexture - to download WWW.movie
/// String - to download WWW.text
///
/// </typeparam>
/// <param name="gameObject">Parent GameObject used to host a temporary instance of the wwwDownloader class.</param>
/// <param name="url">URL of the asset to be downloaded.</param>
/// <param name="callback">Method to be called once the asset has finished downloading.</param>
public static void Download<T>(this GameObject gameObject, string url, Action<T> callback)
{
var downloader = (wwwDownloader)gameObject.AddComponent<wwwDownloader>();
downloader.Download<T>(url,
(param) =>
{
// Destroy the downloader component
GameObject.Destroy(downloader);
// Return downloaded data to the caller
callback(param);
});
}
#endregion
}
#region >>>> Additional Utility Types
/// <summary>
/// Utility class used by the Dowload<T> extension method to download assets.
/// </summary>
public class wwwDownloader : MonoBehaviour
{
/// <summary>
/// Call this method to start downloading.
/// </summary>
public void Download<T>(string url, Action<T> callback)
{
if (string.IsNullOrEmpty(url))
{
Debug.LogWarning(@"Download : url is null or empty.");
// Return a default value of whichever type was supplied to this method
callback(default(T));
}
// Start the downloader
StartCoroutine(DownloadAsset<T>(url, callback));
}
/// <summary>
/// Download worker.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="callback"></param>
/// <returns></returns>
private IEnumerator DownloadAsset<T>(string url, Action<T> callback)
{
object downloadedAsset;
// Get name of the asset type we are going to download
var assetType = typeof(T).ToString();
// Download the asset
var www = new WWW(url);
// Wait for download to complete
yield return www;
// Retrieve correct asset type from the downloader based on the type's name
switch (assetType)
{
case "UnityEngine.AssetBundle":
downloadedAsset = www.assetBundle;
break;
case "UnityEngine.AudioClip":
downloadedAsset = www.audioClip;
break;
case "System.Object":
downloadedAsset = www.bytes;
break;
case "UnityEngine.MovieTexture":
downloadedAsset = www.movie;
break;
case "UnityEngine.Texture":
downloadedAsset = www.texture;
break;
case "System.String":
downloadedAsset = www.text;
break;
default :
downloadedAsset = default(T);
break;
}
// Return downloaded asset
callback((T)downloadedAsset);
}
}
#endregion
So, now we have ourselves a nifty extension method to deal with downloads.
“Big deal. Guy wrote a wrapper class. I need to lie down!” some of you might say. I completely agree. Big deal it is not.
But look how shiny!
#region >>>> Usings
using UnityEngine;
#endregion
public class FooClassOfHugeSignificance : MonoBehaviour
{
private Texture _someTexture;
void Start()
{
// We can load textures (or anything else for that matter) with that nifty extension method that wraps up all the WWW nonsense.
// Step 1 : First we define our URL. It can be anything even local paths work, just change the first parameter to "file://". See Unity documentation on WWW class for a list of supported protocols.
var url = string.Format("{0}{1}", "http://", "images.earthcam.com/ec_metros/ourcams/fridays.jpg");
// Step 2 : Use the game object to host our downloader.
// You can actually pass whichever supported type you want instead of Texture. Currently Unity's WWW class supports downloading AssetBundle, AudioClip, String, Texture, MovieTexture and
// Byte[] (use Object instead then cast to byte[])
gameObject.Download<Texture>(url, (downloadedTexture) =>
{
// Step 3 : This method will get called once the download is complete.
// Assign downloaded texture to a local variable that we can then use in our OnGui() method.
_someTexture = downloadedTexture;
// Note : You can actually use your own callbacks here. Personally I prefer it this way, seems cleaner.
});
}
void OnGUI()
{
// Step 4 : Draw our texture as soon as its done downloading.
if (Event.current.type == EventType.Repaint)
{
if (_someTexture != null)
{
// Draw texture
GUI.DrawTexture(new Rect(400, 400, 200, 200), _someTexture);
}
}
}
}
Cool! Right?
Ok…‘Cool!’ might have been a bit of a stretch, but I still think it’s pretty nice considering you never ever ever have to deal with all the plumbing code for downloads ever again.
To me the biggest advantage of using something like this is the simplicity of end user code. Basically we can get an asset bundle in a single line of code.
gameObject.Download<AssetBundle>(someURL, (downloadedAsset) => { _localAssetVariable = downloadedAsset; });
I think I went a little overboard with self praise here and there. Honestly, didn’t mean to
Anyway I would love to hear what you guys and gals think. Criticism, constructive or otherwise, is always welcome.
Perhaps someone more knowledgeable could point out some pitfalls of this approach or whatnot.
Looking forward to your feedback.
Regards,
Alex