I'm creating a 2d flash game (coded in flex/actionscript 3) where assets are downloaded when they are needed. Currently I have it setup like this:
AssetLoader.as
package
{
import flash.display.Loader;
import flash.net.URLRequest;
public class AssetLoader extends Loader
{
//set vars
private var url:String = "http://test.com/client/assets/";
public function AssetLoader(url:String)
{
Logger.log("AssetLoader request: " + this.url + url);
var request:URLRequest = new URLRequest(this.url + url);
this.load(request);
}
}
}
Then, where I want to load the asset I do the following:
var asset:AssetLoader = new AssetLoader("ships/" + graphicId + ".gif");
asset.contentLoaderInfo.addEventListener(Event.COMPLETE, onShipAssetComplete, false, 0, true);
private function onShipAssetComplete(event:Event):void
{
var loader:Loader = Loader(event.target.loader);
shipImage = Bitmap(loader.content);
shipImage.smoothing = true;
addChild(shipImage);
}
The thing is, that this method doesn't check for already downloaded assets, so it will redownload them the second time the same asset is being requested (I think).
So, what I need is an array where all downloaded assets are stored, and on request the name of this asset is checked for existance in the array. So if it has already been downloaded, that asset from memory must be returned rather than redownloaded.
I could make the assetloader a static class, but I have to wait for the event to fire when it's done downloading the image - so I can't simply let a static function return the corresponding image. Any idea how I should do this?
EDIT for an attempt after comments:
package
{
import flash.display.Loader;
import flash.events.Event;
import flash.net.URLRequest;
public final class AssetManager
{
private static var assets:Object = {};
private static var preUrl:String = Settings.ASSETS_PRE_URL;
public static function load(postUrl:String):*
{
if (assets[postUrl])
{ //when the asset already exists
//continue
}
else
{ //the asset still has to be downloaded
var request:URLRequest = new URLRequest(preUrl + postUrl);
var loader:Loader = new Loader();
loader.load(request);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,
function(event:Event):void
{
var loader:Loader = Loader(event.target.loader);
assets[postUrl] = loader.content;
}, false, 0, true);
}
}
}
}
EDIT2: another attempt
package
{
import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.net.URLRequest;
public final class AssetManager
{
private static var assets:Object = {};
private static var preUrl:String = Settings.ASSETS_PRE_URL;
public static function load(postUrl:String):*
{
if (assets[postUrl])
{ //the asset already exists
var dispatcher:EventDispatcher = new EventDispatcher();
dispatcher.dispatchEvent(new CustomEvent(CustomEvent.LOAD_COMPLETE, assets[postUrl]));
}
else
{ //the asset still has to be downloaded
var request:URLRequest = new URLRequest(preUrl + postUrl);
var loader:Loader = new Loader();
loader.load(request);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,
function(event:Event):void
{
var loader:Loader = Loader(event.target.loader);
assets[postUrl] = loader.content;
var dispatcher:EventDispatcher = new EventDispatcher();
dispatcher.dispatchEvent(new CustomEvent(CustomEvent.LOAD_COMPLETE, assets[postUrl]));
}, false, 0, true);
}
}
}
}
Then, I try the following:
var asset:AssetManager = AssetManager.load("ships/" + graphicId + ".gif");
asset.addEventListener(CustomEvent.LOAD_COMPLETE, onShipAssetComplete, false, 0, true);
But get an error, "undefined method addEventListener by a reference of the type static AssetManager" (roughly translated).
You could add a static object (used as a dictionary with urls for assets as keys and the content for assets as values) in the AssetLoader class and in the same time keep using the class in the way you're using it right now.
private static var assets:Object = {};
The difference would be that your class would need to check against that static object if the URL for the content has already been requested previously. If it has, dispatch the complete event immediately. If it hasn't, follow the normal routine and don't forget to populate your static object with the newly loaded asset.
Update:
This is a quick example of what I meant. I haven't had time to test this, but it should work.
Note:
You must invoke the loadAsset() method of the AssetLoader instances you create in order to actually load the asset. This is consistent with the way the Loader class we're extending works.
You should always add all event listeners BEFORE invoking the loadAsset() method. In your question you're calling the load() method from within the constructor and only afterwards add the event listener for Event.COMPLETE. This could produce strange results.
Here's the code:
package
{
import flash.display.Loader;
import flash.events.Event;
import flash.net.URLRequest;
public class AssetLoader extends Loader
{
private static const BASE_URL:String = 'http://test.com/client/assets/';
public static var storedAssets:Object = {};
private var assetURL:String;
private var urlRequest:URLRequest;
private var cached:Boolean = false;
public function AssetLoader(url:String):void
{
trace('Loading: ' + url);
assetURL = url;
if (storedAssets[assetURL] != null)
{
cached = true;
trace('Cached');
}
else
{
trace('Loading uncached asset');
urlRequest = new URLRequest(BASE_URL + assetURL);
contentLoaderInfo.addEventListener(Event.COMPLETE, OnAssetLoadComplete);
}
}
public function loadAsset():void
{
if (cached)
loadBytes(storedAssets[assetURL]);
else
load(urlRequest);
}
private function OnAssetLoadComplete(event:Event):void
{
storedAssets[assetURL] = contentLoaderInfo.bytes;
trace('Loaded ' + contentLoaderInfo.bytesLoaded + ' bytes');
}
}
}
Update 2:
Here's how one would use the class above:
var assetLdr:AssetLoader = new AssetLoader("ships/" + graphicId + ".gif");
assetLdr.contentLoaderInfo.addEventListener(Event.COMPLETE, onShipAssetComplete);
assetLdr.loadAsset();
private function onShipAssetComplete(event:Event):void
{
var shipImage:Bitmap = Bitmap(event.target.loader.content);
// Do stuff with shipImage
}