AssetLoader Robotlegs Tutorial

Posted 20 August 2010 by

Trying to think of a good example/tutorial for AssetLoader isn’t easy, because is so versatile. If I had so show you everything you can do with AssetLoader, you’ll be reading for 2 days straight! So I’m going to keep it super simple, for your sake and mine. For this post I’m assuming you have good Robotlegs knowledge, if not – please get with it!

For this example we’re building a small application that loads in HTML, CSS and a header IMAGE. I just pulled out the most important parts relating to AssetLoader; Mapping, Adding, Managing IDs and Collecting. Sources ZIP available here and at the bottom of this post.

Mapping

I would advise mapping your primary AssetLoader as a singleton.

package mu.sample
{
	import mu.sample.controller.ConfigLoadedCommand;
	import mu.sample.controller.StartupCompleteCommand;
	import mu.sample.utils.FlashVars;
	import mu.sample.view.Canvas;
	import mu.sample.view.CanvasMediator;

	import org.assetloader.AssetLoader;
	import org.assetloader.core.IAssetLoader;
	import org.assetloader.events.AssetLoaderEvent;
	import org.robotlegs.base.ContextEvent;
	import org.robotlegs.mvcs.Context;

	import flash.display.DisplayObjectContainer;

	/**
	 * @author Matan Uberstein
	 */
	public class AssetLoaderSampleContext extends Context
	{
		public function AssetLoaderSampleContext(contextView : DisplayObjectContainer = null, autoStartup : Boolean = true)
		{
			super(contextView, autoStartup);
		}

		override public function startup() : void
		{
			//Commands
			commandMap.mapEvent(ContextEvent.STARTUP_COMPLETE, StartupCompleteCommand, ContextEvent, true);

			//Not required if you aren't passing a config FILE to your IAssetLoader.
			commandMap.mapEvent(AssetLoaderEvent.CONFIG_LOADED, ConfigLoadedCommand, AssetLoaderEvent, true);

			//Services
			injector.mapSingletonOf(IAssetLoader, AssetLoader);

			//Views
			mediatorMap.mapView(Canvas, CanvasMediator);

			//Utils
			injector.mapSingleton(FlashVars);

			//Dispatches ContextEvent.STARTUP_COMPLETE
			super.startup();
		}
	}
}

Manage your asset IDs

AssetLoader relies on IDs to operate, you need to give each asset/group an ID in order for you to retrieve your payloads or access the loader. I would recommend creating a class that contains public static constants with their string values.

package mu.sample.model.ids
{
	public class AssetId
	{
		public static const HEADER_IMAGE : String = "HEADER_IMAGE";
		public static const GLOBAL_STYLESHEET : String = "GLOBAL_STYLESHEET";
		public static const HTML_BODY : String = "HTML_BODY";
	}
}

Adding assets to the queue

There are few ways of adding assets to your loading queue:

  • add – Pass your own URLRequest.
  • addLazy – URLRequest created automatically, thus you only pass the URL.
  • addUnit – Construct your own ILoadUnit.
  • addConfig – Parses XML or JSON representation of asset queue. Can also accept an URL or String format of data. Please see the wiki section on Github.

For this example we will only be using addLazy, but in the source bundle I’ve added the addConfig equivalent.

package mu.sample.controller
{
	import mu.sample.model.ids.AssetId;
	import mu.sample.utils.FlashVars;
	import mu.sample.view.Canvas;

	import org.assetloader.core.IAssetLoader;
	import org.robotlegs.mvcs.Command;

	import flash.display.StageAlign;
	import flash.display.StageScaleMode;

	/**
	 * @author Matan Uberstein
	 */
	public class StartupCompleteCommand extends Command
	{

		[Inject]
		public var assetLoader : IAssetLoader;

		[Inject]
		public var flashvars : FlashVars;

		override public function execute() : void
		{
			contextView.stage.scaleMode = StageScaleMode.NO_SCALE;
			contextView.stage.align = StageAlign.TOP_LEFT;

			//Configure my assets and loading queue.

			//Just for the hell of it.
			assetLoader.numConnections = 1;

			// Note: Not passing type parameter, because these will be automatically detected.
			assetLoader.addLazy(AssetId.HEADER_IMAGE, flashvars.basePath + "images/header.png");
			assetLoader.addLazy(AssetId.GLOBAL_STYLESHEET, flashvars.basePath + "data/default.css");
			assetLoader.addLazy(AssetId.HTML_BODY, flashvars.basePath + "data/body.html");

			//PLEASE NOTE:
			//All the above could have been replace by:
			//assetLoader.addConfig(flashvars.basePath + "data/assets.xml");
			//Check out the xml layout of the assets.xml file in the data folder.

			//Adding my primary view the stage to ensure that it's already listening for the events before they fire.
			contextView.addChild(new Canvas());

			//Telling assetloader to start!
			assetLoader.start();
		}
	}
}

Collecting your assets

AssetLoader automatically dispatches into you shell’s eventDispatcher, which means that you can map commands and/or use the eventMap within Actors/Mediators. For this example all we need to do listen to when our assets are loaded. There are a few events we can listen to:

  • AssetLoaderEvent.ASSET_LOADED – dispatches when any one of your assets are finished loading. Always dispatches no matter what the asset type is.
  • Type related events – e.g. When you have an IMAGE type asset, ImageAssetEvent.LOADED will dispatch. Advantage here is that these related events carry strongly typed payloads.
  • Extracting the ILoader from the IAssetLoader and listening to the Event.COMPLETE – Each ILoader (could be anything, ImageLoader, XMLLoader, CSSLoader, etc) dispatches Event.COMPLETE when it has finished loading. Advantage here is that you don’t have to check the id of the asset inside the handler.

In this particular case we could have only listened to the AssetLoaderEvent.COMPLETE event, because it’s a simple case.

Below is an example of each:

package mu.sample.view
{
	import mu.sample.model.ids.AssetId;

	import org.assetloader.core.IAssetLoader;
	import org.assetloader.core.ILoader;
	import org.assetloader.events.AssetLoaderEvent;
	import org.assetloader.events.ImageAssetEvent;
	import org.robotlegs.mvcs.Mediator;

	import flash.events.Event;

	/**
	 * @author Matan Uberstein
	 */
	public class CanvasMediator extends Mediator
	{
		[Inject]
		public var view : Canvas;
		[Inject]
		public var assetLoader : IAssetLoader;

		override public function onRegister() : void
		{
			// Extract ILoader
			var cssLoader : ILoader = assetLoader.getLoader(AssetId.GLOBAL_STYLESHEET);
			eventMap.mapListener(cssLoader, Event.COMPLETE, cssLoaded_handler, Event);

			// Listen to generic AssetLoaderEvent.
			eventMap.mapListener(eventDispatcher, AssetLoaderEvent.ASSET_LOADED, textLoaded_handler, AssetLoaderEvent);

			// Listen to type specific events.
			eventMap.mapListener(eventDispatcher, ImageAssetEvent.LOADED, imageLoaded_handler, ImageAssetEvent);

			view.init();
		}

		/**
		 * This will only fire once, because we listening directly to the loader we need.
		 *
		 * Pros:
		 * Only fires once.
		 *
		 * Cons:
		 * Casting, strong data type lost.
		 */
		protected function cssLoaded_handler(event : Event) : void
		{
			var cssLoader : ILoader = ILoader(event.target);
			view.styleSheet = cssLoader.data;

			eventMap.unmapListener(cssLoader, Event.COMPLETE, cssLoaded_handler, Event);
		}

		/**
		 * This will fire for all assets, thus we need to check the id before reacting.
		 *
		 * Pros:
		 * It works... I guess... hehe
		 *
		 * Cons:
		 * Fires for all assets - just a con in this case.
		 * ID checking.
		 *
		 */
		protected function textLoaded_handler(event : AssetLoaderEvent) : void
		{
			if(event.id == AssetId.HTML_BODY)
			{
				view.text = event.data;
				eventMap.unmapListener(eventDispatcher, AssetLoaderEvent.ASSET_LOADED, textLoaded_handler, AssetLoaderEvent);
			}
		}

		/**
		 * This will fire for all IMAGE type assets, thus we need to check the id before reacting.
		 *
		 * Pros:
		 * Only fires for related type assets.
		 *
		 * Cons:
		 * ID checking.
		 */
		protected function imageLoaded_handler(event : ImageAssetEvent) : void
		{
			if(event.id == AssetId.HEADER_IMAGE)
			{
				view.logo = event.bitmap;
				eventMap.unmapListener(eventDispatcher, ImageAssetEvent.LOADED, imageLoaded_handler, ImageAssetEvent);
			}
		}
	}
}

Sample Application

Built with FDT, Robotlegs, minimalcomps, FlashVarsDynamic and AssetLoader of course.

Source

Post Details

  • Dave

    This looks like a terrific class. Thanks for sharing!

    I’ve noticed the loaded png files are loosing their transparency. Any thoughts on how to preserve the png alpha?

    Thanks,
    Dave

  • Dave

    Nevermind! I dug into the source and found the imageParams I can pass an array of :

    var transparent : IParam = new Param(Param.TRANSPARENT, true);
    var fillColor : IParam = new Param(Param.FILL_COLOR, 0×0);
    var smoothing : IParam = new Param(Param.SMOOTHING, true);

    in the addLazy call:

    assetLoader.addLazy(AssetId.HEADER_IMAGE, “/images/header.png”, AssetType.IMAGE, imageParams);

    It seems that I must include the FILL_COLOR in order to use the TRANSPARENT Param.

    Thanks for having the options!

  • http://doesflash.com Matan Uberstein

    :) Glad you got the solution!

  • StimuliTV

    Hi Matan,

    Nice example thanks.
    One issue though with a custom setup.
    When i setup this project in flash builder it works fine but if i remove the AssetLoader-v1.1.2.swc and drop in your org.assetloader source code as a replacement (into the source for this project) the AssetLoaderEvent is nowhere to be seen. Its not a part of the v2.0.0-1 zip, there is no events package or events.

    I take it the 1.1.2 swc doesnt use signals? so should i replace the event with the LoaderSignal??

    Best

    • http://doesflash.com Matan Uberstein

      Hi,

      Yeah, v1 did not use signals, v2 does. So in v2 you won’t find any events. I’m busy writing a new tutorial on everything. You can check the ASdocs for the Signal specifics. But here are the basics:

      var xmlLoader : ILoader = new XMLLoader("gallery-config", new URLRequest("http://some.where.com/assets/gallery-config.xml&quot ;) );
      
      xmlLoader.onError.add(xmlLoader_error_handler);
      xmlLoader.onProgress.add(xmlLoader_progress_handler);
      xmlLoader.onComplete.add(xmlLoader_complete_handler);
      
      xmlLoader.start();
      
      function xmlLoader_error_handler(signal:ErrorSignal) : void
      {
      	trace(signal.type + " | " + signal.message);
      }
      
      function xmlLoader_progress_handler(signal:ProgressSignal) : void
      {
      	trace(signal.progress + "%");
      }
      
      function xmlLoader_complete_handler(signal:LoaderSignal, xml:XML) : void
      {
      	trace(xml);
      }
      

      Let me know if you need any more help! Btw, thanks for using the AssetLoader lib. :) I would love to get some feed back from you after switching to v2.

  • Daniel

    Hey Matt,

    thanks for the great work on assetLoader, really makes life easier :) Am I correct in the assumption that v 2.x with signals does not support handling certain type assets anymore? If I am not overseeing something, I can now either get the specific loader for a certain id or use the assetLoader Singleton only. The different AssetEvents used to be very practical for my Mediators. Any hints?

    Regards,
    *D

    • http://doesflash.com Matan Uberstein

      Hi Daniel,

      Thanks for the great feedback. :) AssetLoader v2 still supports all the asset types. Unfortunately by dropping the Event system, those handy “specific” events (e.g. XMLAssetEvent) are lost.

      I might look into building a assetloader-robotlegs-utilities lib to get those specific payload dispatched into your RL project. This will only be sometime next year.

      Like you said, you can grab your ILoader from your primary IAssetLoader within your Mediator or just grab the payload directly via the “getAsset” method.

      var xml : XML = assetloader.getAsset("some-id");

      Be sure to join us on the 17th for my online meeting. http://bit.ly/gRLHzs

      Thanks! :mrgreen: