Pixlib – Tut 03 – How to load assets at runtime


Hello there.

As said in the title this tutorial will help you to load assets at runtime. At the same time we will see how to use FlashDevelop and MTASC to create a swf. So no need to open flash anymore...

Ok first, if you didn't please look at the first tutorial which give you the basis of the pixlib framework.

This tutorial will use the source files of the first tutorial as a starting point. You can download those files from here

First Tutorial Sources Download the first tutorial sources Unknown

The sources of this tutorial
Tutorial Sources Download this tutorial's sources Unknown

 

I. Refreshing our memory

So remember, we had a fla importing the Application.as class and then creating an instance of it to start the whole application.

Actionscript:
  1. import net.webbymx.projects.tutorial01.Application;
  2. var apz : Application = new Application( this );

The fla had the assets in it's library so we were doing in the Application.as

Actionscript:
  1. //  Instantiating the views
  2.     var digital : MovieClip = this.attachMovie("mc_digital", "mc_digital", 0 );
  3.     var vDigital  : ViewDigital = new ViewDigital ( digital );

to instantiate a view.

 

II. SWFMILL and MTASC

Now, we don't want to use flash anymore ( anyway for the development side ).

We will use 2 tools:

  • SWFMILL to create swfs ( empty or not, see the project page for more info please )
  • MTASC to compile our classes with and push them in the swf created by SWFMILL

If you don't know how to use or integrate those great tools to your editor, go on their respective project page and / or on the project page of your editor.

For my case, I'm using FlashDevelop a great FREE editor ( only for window user sorry :/ ). It has MTASC and SWFMILL in it, and it's as simple as clicking a button to compile your project.

 

III. Show me the sh*t!!!

Ok, ok...

a. Pixlib Classes used

We will use 3 classes that are part of the Pixlib framework.

  • The first one is the ConfigLoader, which helps you loading the XML setting up your application
  • The second one is the GraphicLib, which helps you loading an asset in your application.
  • The third one is the LibStack, which is a download manager

Why that?
First we need to load an xml to know where our assets are.
Then we will use the LibStack, push in it all the GraphicLib created ( one for each asset ) and start the download process.

By the way if you dunno how works the ConfigLoader class have a look here please.

b. A typical Application.as

Here is the code of a typical Application.as class. I'm now always using this as a template for all my projects ( until further improvement as usual :) )
It's a bit long, but it's just for you to have a quick look. We will go anyway in details in the code...

Actionscript:
  1. //  Debug
  2. import com.bourre.log.Logger;
  3. import com.bourre.log.LogLevel;
  4. import com.bourre.log.PixlibDebug;
  5. import com.bourre.utils.LuminicTracer;
  6.  
  7. //  Models
  8. import com.bourre.core.Model;
  9. import net.webbymx.projects.tutorial03.models.*;
  10.  
  11. //  Views
  12. import com.bourre.visual.MovieClipHelper;
  13. import net.webbymx.projects.tutorial03.views.*;
  14.  
  15. //  Controller
  16. import net.webbymx.projects.tutorial03.commands.Controller;
  17.  
  18. //  Personnal EventBroadcaster
  19. import net.webbymx.projects.tutorial03.events.XEventBroadcaster;
  20.  
  21. //  Events
  22. import com.bourre.events.BasicEvent;
  23. import com.bourre.events.IEvent;
  24. import net.webbymx.projects.tutorial03.events.EventList;
  25.  
  26. //  ConfigLoader
  27. import com.bourre.data.libs.ConfigLoader;
  28. import com.bourre.data.libs.XMLToObjectDeserializer;
  29.  
  30. //  Libstact, GraphicLib
  31. import com.bourre.data.libs.LibStack;
  32. import com.bourre.data.libs.GraphicLib;
  33. import com.bourre.data.libs.GraphicLibEvent;
  34.  
  35. //  tweening
  36. import com.mosesSupposes.fuse.*;
  37.  
  38. class net.webbymx.projects.tutorial03.Application
  39.     extends MovieClip {
  40.        
  41. /* ****************************************************************************
  42. * PRIVATE STATIC VAR
  43. **************************************************************************** */
  44.     private static var _oI  : Application;
  45.    
  46.  
  47. /* ****************************************************************************
  48. * PRIVATE VARIABLES
  49. **************************************************************************** */
  50. //  VAR
  51.     private var _oConf  : Object
  52.  
  53.    
  54. /* ****************************************************************************
  55. * CONSTRUCTOR
  56. **************************************************************************** */
  57.     private function Application(container) {
  58.     //  the movieclip is transtyped as a application object
  59.         container.__proto__ = this.__proto__;
  60.         container.__constructor__ = Application;
  61.         this = container;
  62.         _oI = this;
  63.        
  64.     //  init the object
  65.         _init();
  66.        
  67.     //  load the conf
  68.         _loadConf();
  69.     }
  70.    
  71.     public static function getInstance() : Application {
  72.         if ( _oI == undefined ) {
  73.             if ( arguments[ 0 ] == undefined || typeof( arguments[ 0 ] ) != "movieclip" ) PixlibDebug.FATAL( "Cannot create the Application object. You need to pass a MovieClip as a parameter" );
  74.             else var apz : Application = new Application( arguments[ 0 ] );
  75.         }
  76.         return _oI;
  77.     }
  78.    
  79.     public static function main( mc : MovieClip ) : Void {
  80.         var apz : Application = Application.getInstance( mc );
  81.     }
  82.    
  83.  
  84. /* ****************************************************************************
  85. * PRIVATE FUNCTIONS
  86. **************************************************************************** */
  87. /**
  88. * Init the Application
  89. * @param    Void
  90. */ 
  91.     private function _init() : Void {
  92. Logger.LOG( "Application :: _init" );
  93.  
  94.         Stage.align = "TL";
  95.  
  96.     //  register the ZigoEngine
  97.         ZigoEngine.register( PennerEasing, FuseItem, FuseFMP );
  98.         ZigoEngine.EASING = "linear";
  99.        
  100.     //  init the debugger
  101.         Logger.getInstance().addLogListener( LuminicTracer.getInstance() );
  102.        
  103.     //  create the model
  104.         var mClock : ModelClock = new ModelClock();
  105.  
  106.     //  init the controller with our custom EventBroadcaster class
  107.         Controller.getInstance( XEventBroadcaster.getInstance() );
  108.     }
  109.  
  110.    
  111. /**
  112. * Load the congif.xml file
  113. * @param    Void
  114. */
  115.     private function _loadConf( Void ) : Void {
  116. Logger.LOG( "Application :: _loadConf" );
  117.  
  118.     //  Define some properties of the deserializer
  119.         XMLToObjectDeserializer.DESERIALIZE_ATTRIBUTES = true;
  120.         XMLToObjectDeserializer.PUSHINARRAY_IDENTICAL_NODE_NAMES = true;
  121.        
  122.     //  creating the object holding the result for a map
  123.         _oConf = new Object();
  124.    
  125.     //  creating the config loader
  126.         var _cfLoader : ConfigLoader = new ConfigLoader( _oConf );
  127.     //  define the callback once the xml is loaded
  128.         _cfLoader.addEventListener( ConfigLoader.onLoadInitEVENT, this, loadLoader );
  129.     //  load by default the "config.xml" file
  130.         _cfLoader.load();
  131.     }
  132.  
  133.  
  134.     public function _loadAssets () : Void {
  135. Logger.LOG( "Application :: loadAssets" );
  136.  
  137.     //  total bytes loaded
  138.         var totalBytes : Number = 0;
  139.        
  140.     //  Loading everything
  141.         var libContainer : MovieClip = this["__libContainer"];
  142.         var libs : LibStack = new LibStack();
  143.    
  144.     //  get the array of assets to load
  145.         var aAssets : Array = _oConf.assets.asset;
  146.         for( var i = 0; i <aAssets.length; ++i ) {
  147.         //  create the graphic lib
  148.             var gl : GraphicLib = new GraphicLib( libContainer, aAssets[ i ].depth, false );
  149.         //  define callbacks
  150.             gl.addEventListener( GraphicLib.onLoadInitEVENT, this, onAssetLoaded );
  151.             gl.addEventListener( GraphicLib.onLoadProgressEVENT, this, onAssetProgress );
  152.         //  add to the LibStack
  153.             libs.enqueue( gl, aAssets[ i ].view, _oConf.paths.assets + aAssets[ i ].url );
  154.         //  update the totalBytes
  155.             totalBytes += aAssets[ i ].size;
  156.         }
  157.        
  158.     //  set the total bytes
  159.         var vLoader : ViewPreloader = ViewPreloader( MovieClipHelper.getMovieClipHelper( ViewList.VIEW_PRELOADER ) );
  160.         vLoader.setTotalBytes( totalBytes );
  161.  
  162.     //  define the call back events
  163.         libs.addEventListener ( LibStack.onLoadCompleteEVENT, this );
  164.         libs.addEventListener( LibStack.onTimeOutEVENT, this );
  165.  
  166.     //  Start the download
  167.         libs.execute();
  168.     }
  169.  
  170. /**
  171. * Build the Application
  172. * @param    Void
  173. */
  174.     private function _build( Void ) : Void {
  175. Logger.LOG( "Application :: _build" );
  176.    
  177.     //  you can hide the preloader from inside a command ( like in a command StartApplication )
  178.         var vLoader : ViewPreloader = ViewPreloader( MovieClipHelper.getMovieClipHelper( ViewList.VIEW_PRELOADER ) );
  179.         vLoader.fadeout();
  180.  
  181.     //  create views
  182.         var vDigital  : ViewDigital = new ViewDigital ( ViewList.VIEW_DIGITAL );
  183.         var vAnalog  : ViewAnalog = new ViewAnalog ( ViewList.VIEW_ANALOG );
  184.         var vTools : ViewTools = new ViewTools ( ViewList.VIEW_TOOLS );
  185.  
  186.     //  views listening to the model
  187.         var mClock : ModelClock = ModelClock( Model.getModel( ModelList.MODEL_CLOCK ) );
  188.         mClock.addListener( vDigital );
  189.         mClock.addListener( vAnalog );
  190.         mClock.addListener( vTools );
  191.  
  192.     //  The clock start playing
  193.         XEventBroadcaster.getInstance().broadcastEvent( new BasicEvent( EventList.START_CLOCK ) );
  194.     }
  195.    
  196.  
  197. /* ****************************************************************************
  198. * PUBLIC FUNCTIONS - CALLBACKS
  199. **************************************************************************** */
  200. /**
  201. * load the loader ( funny :/ )
  202. * @param    e
  203. */
  204.     public function loadLoader( e : IEvent ) : Void {
  205. Logger.LOG( "Application :: loadAssets" );
  206.     //  this movieclip will be the main container for all the assets loaded
  207.         var libContainer = createEmptyMovieClip( "__libContainer" , this.getNextHighestDepth() );
  208.    
  209.     //  Creating the GraphicLib for the prelaoder
  210.         var gl : GraphicLib = new GraphicLib( libContainer, _oConf.assets.preloader.depth, false );
  211.     //  define the callbacks
  212.         gl.addEventListener( GraphicLib.onLoadInitEVENT, this, onLoaderLoaded );
  213.     /*  define the name of the library - the same as the view name
  214.     *   thus when we are creating the view, the view will look in all the GraphicLib will find one
  215.     *   with the same name and will use it */
  216.         gl.setName( _oConf.assets.preloader.view );
  217.     //  load the preloader
  218.         gl.load( _oConf.paths.assets + _oConf.assets.preloader.url );
  219.  
  220.     }
  221.    
  222. /**
  223. * call back once the graphic of the laoder is called
  224. * @param    e
  225. */
  226.     public function onLoaderLoaded( e : GraphicLibEvent ) : Void {
  227. Logger.LOG( "Application :: onLoaderLoaded" );
  228.     //  create the view, will link automatically to the graphic
  229.         var vLoader : ViewPreloader = new ViewPreloader( e.getName() );
  230.         vLoader.show();
  231.        
  232.     //  start the download process
  233.         _loadAssets();
  234.     }
  235.  
  236.  
  237. /**
  238. * Update the loader view
  239. * @param    e
  240. */
  241.     public function onAssetProgress( e : GraphicLibEvent ) : Void {
  242.         var vLoader : ViewPreloader = ViewPreloader( MovieClipHelper.getMovieClipHelper( ViewList.VIEW_PRELOADER ) );
  243.         vLoader.onLoadProgress( e.getLib().getBytesLoaded() );
  244.     }
  245.    
  246.    
  247. /**
  248. * Update the loader view
  249. * @param    e
  250. */
  251.     public function onAssetLoaded ( e : GraphicLibEvent ) : Void  {
  252. Logger.LOG( "Application :: onAssetLoaded : " + e.getName() );
  253.     //  e.getView().stop();
  254.         var vLoader : ViewPreloader = ViewPreloader( MovieClipHelper.getMovieClipHelper( ViewList.VIEW_PRELOADER ) );
  255.         vLoader.addBytes( e.getLib().getBytesTotal() );
  256.     }
  257.  
  258. /**
  259. * Once everything is loaded
  260. */
  261.     public function onLoadComplete() : Void {
  262. Logger.LOG( "Application :: onLoadComplete" );
  263.         _build();
  264.     }
  265.  
  266. /* ****************************************************************************
  267. * GETTER & SETTER
  268. **************************************************************************** */
  269. }

Ok let's see the code in details now.

As usual my Application.as when created is calling the _init() function, which is setting up ( here ) the fuse tween engine, the Looger, and controller using our custom EventBroadcaster ( why using a custom EventBroadcaster is explain in the first tutorial )

LOADING THE CONFIGURATION
Then following the process said above, I'm first loading the config.xml file

Actionscript:
  1. /**
  2. * Load the congif.xml file
  3. * @param    Void
  4. */
  5.     private function _loadConf( Void ) : Void {
  6. Logger.LOG( "Application :: _loadConf" );
  7.  
  8.     //  Define some properties of the deserializer
  9.         XMLToObjectDeserializer.DESERIALIZE_ATTRIBUTES = true;
  10.         XMLToObjectDeserializer.PUSHINARRAY_IDENTICAL_NODE_NAMES = true;
  11.        
  12.     //  creating the object holding the result for a map
  13.         _oConf = new Object();
  14.    
  15.     //  creating the config loader
  16.         var _cfLoader : ConfigLoader = new ConfigLoader( _oConf );
  17.     //  define the callback once the xml is loaded
  18.         _cfLoader.addEventListener( ConfigLoader.onLoadInitEVENT, this, loadLoader );
  19.     //  load by default the "config.xml" file
  20.         _cfLoader.load();
  21.     }

You should know this already, otherwise go look the second tutorial

LOADING THE LOADER
So when our config.xml is loaded the function loadLoader is called

Actionscript:
  1. /**
  2. * load the loader ( funny :/ )
  3. * @param    e
  4. */
  5.     public function loadLoader( e : IEvent ) : Void {
  6. Logger.LOG( "Application :: loadAssets" );
  7.     //  this movieclip will be the main container for all the assets loaded
  8.         var libContainer = createEmptyMovieClip( "__libContainer" , this.getNextHighestDepth() );
  9.    
  10.     //  Creating the GraphicLib for the prelaoder
  11.         var gl : GraphicLib = new GraphicLib( libContainer, _oConf.assets.preloader.depth, false );
  12.     //  define the callbacks
  13.         gl.addEventListener( GraphicLib.onLoadInitEVENT, this, onLoaderLoaded );
  14.         gl.addEventListener( GraphicLib.onLoadProgressEVENT, this );
  15.     /*  define the name of the library - the same as the view name
  16.     *   thus when we are creating the view, the view will look in all the GraphicLib will find one
  17.     *   with the same name and will use it */
  18.         gl.setName( _oConf.assets.preloader.view );
  19.     //  load the preloader
  20.         gl.load( _oConf.paths.assets + _oConf.assets.preloader.url );
  21.     }

Ok we are entering in the new stuff...

So here ( line 207 ) we are creating a container that will contain all the asset loaded.

Then we are creating our GraphicLib. GraphicLib helps you to load external assets into your application. To create a new GraphicLib you need to give 3 parameters. The first one, it's parent MovieClip, second one the depth it will be load in, and a final ( and optional ) defining if you want your graphic to show automatically once loaded.

Actionscript:
  1. var gl : GraphicLib = new GraphicLib( libContainer, _oConf.assets.preloader.depth, false );

A GraphicLib is broadcasting 3 events:

  • onTimeOut: if a onLoadProgress event was not receive after a certain time ( by default 10s, but you can set it using the setTimeOut() function ) this event will be triggered
  • onLoadProgress: this event will be called everytime the application is receiving datas
  • onLoadInit: this event will be triggered when the download process of the GraphicLib will be successfully done

So here we are adding the Application object as a listener to the events sent by the GraphicLib.

Then we are setting the name of the GraphicLib. Why that?
When you are creating a MovieClipHelper you have two choices. The first one is passing a name ( used to get the MovieClipHelper using the MovieClipHelper.getMovieClipHelper() function ) and the reference of the clip that will be used ( the one going in the view property ) OR you can just pass a name. If you do so, the MovieClipHelper will look automatically in all the GraphicLib loaded for the name matching his and will use this GraphicLib as a reference to the _view property. Nice :)
That's why here I'm givgin the name of the preloader view ( taken from the xml file ).

Then we are starting the loading process for the preloader, giving the path of the asset to load

Actionscript:
  1. //  load the preloader
  2.         gl.load( _oConf.paths.assets + _oConf.assets.preloader.url );

Please check the ViewPreloader.as and the view_preloader.fla ( which is only the design part ) files to see how it is working.
I will not go into this during this tutorial... The only thing you need to know is that we have 3 methods that we will use. The setTotalBytes that is setting the total bytes that will be loaded, the addBytes that add bytes to loaded bytes and onLoadProgress that update the text and the progress bar.

So ok, our preloader is loaded now. So the function onLoaderLoaded() will be triggered ( remember we've set this up line 212, by adding the Application object as a listener to the GraphicLib object created ).
This function is showing the loader and start loading the assets by calling _loadAssets().

LOADING ALL THE ASSETS
This is the most interesting part. Here we will use the LibStack to enqueue all the swf we want to load using the GraphicLib objects.

First we are setting up the totalBytes to 0. This variable will get the total of bytes that will be loaded.

How? In our xml file, for each swf, we have a node called size that specify the weight of the swf. So as we will read the infos for each swf we will add the size to the totalBytes variable.

Why? Why adding the size in our config.xml? Because you can't know BEFORE starting the loading process how big the file will be. So by knowing it BEFORE ( using this way ) our preloader will be better, because it will be able to display the right % loaded out of the WHOLE. Instead of saying eg " loading 80% of file 02 / 09", which basically say nothing cause you dunno how big are the other files so you dunno how long you will wait, it will say "loading website 10%". Of course you could add as well the file number ( like 02 / 09 ) if you wish.

Ok back to the code.

We are creating a reference to our __libContainer MovieClip ( as it will be used as the container for our assets )

We are creating our LibStack object ( the download manager )

Then we are looping in all our assets ( btw we are able to loop in the asset array made by the XMLTOObjectDeserializer because we have set up the XMLTOObjectDeserializer.PUSHINARRAY_IDENTICAL_NODE_NAMES static var to true ).

For each loop ( asset ) we are:

  • creating a GraphicLib ( that knows where the asset will be loaded and if it should automatically show or not )
  • setting the Application as a listener to the GraphicLib newly created
  • adding the newly created GraphicLib to the LibStack. The main difference is here. We are pushing in the LibStack the GraphicLib object, its name and the path of the asset to load.
  • adding the size of the files to the totalBytes variable

We are setting then the totalBytes of our loader ( then it will be able to get the % of the whole )
We are setting up the Application object as a listener to certain LibStack events

And finally we are starting the download process, by calling OurLibStack.execute().
This function will start downloading the first GraphicLib we have pushed. Once done, the second and so on...

Once everything is loaded the LibStack will dispatch the onLoadCompleteEVENT event. The onLoadComplete will be called, building our website. Here we are jsut creating the views, and passing them the same name we have passed to the GraphicLib ( remember that the MovieClipHelper will automatically look for a GraphicLib having the same name to use it as its _view property ).

That's nice. But to get the % of the whole loaded it's not over.
We've seen that for our GraphicLib objects created we have set up the Application object as an event listener.
We need to write some code in those to interact with the loader.

First the onAssetLoaded. This function is adding the size of the asset to the loadedBytes of the loader.

Second in the onAssetProgress, we will send to the loader the bytes loaded. The loader will display the % of the whole depending on the already loaded bytes + the one sent.

So let's say you are loading the first asset. The loadedBytes are 0. onAssetProgress, we are getting the number of bytes loaded ( 10000 ) that we are sending to the loader using the onLoadProgress. The loader will have 0 + 10000 bytes. Now on the other onAssetProgress we are having now 20000, we are sending this value to the loader which will have 0 + 20000, and so on. Once the asset is fully loaded we are updating the loadedBytes of the preloader. Let's say our asset was 25000 heavy. The loadedBytes of your loader will be now 25000. Then loading the next asset the loader will calculate the % on the 25000 + bytes basis.

I hope that's clear lol :)

If it's not errrr... look at the source files or / and post a comment ...

C ya

No related posts.

, , , , ,

  1. #1 by Matthieu on May 2, 2007 - 7:30 pm

    Thanks for you tut.

    I will now to try to update my website with the libstack of pixlib.

  2. #2 by pml on May 9, 2007 - 2:41 am

    Thanks for the share, it's really usefull to me.
    May be i'm wrong but i noticed that:
    - in Application.as, the instance does listen to any onLoadProgress from the loading of the loader (no onLoadProgress method) so the line:
    gl.addEventListener( GraphicLib.onLoadProgressEVENT, this );
    seems to need to be forgotten

    - the fuse in the loader view seems to be missing his target: no clip instance named "mc_head" in the original view_preloader.fla, i just corrected with a single element fuse...
    Thanks again

  3. #3 by zeflasher on May 9, 2007 - 12:37 pm

    Yes you are right pml.
    For the event I've forgot to remove it. We don't need it as we are loading the loader ( always weird when I'm writing this:) )
    For the fuse you are right too. I've forgot to remove it too ( I've taken the code from real job and have deleted what was not needed, didn't see this one :/ )

    Anyway, I've updated the files
    and the tutorial...

  4. #4 by goliatone on September 8, 2007 - 4:49 am

    hi there,
    that was a great tutorial, thnxs for the effort and for sharing it with us!
    Can´t wait to see more stuff!!

    regards,
    goliatone

  5. #5 by isambard on September 19, 2007 - 5:22 am

    I just want to say THANKS SO MUCH for both the tutorials and the template implementations; i may have got there eventually, but it looked pretty impenetrable to me before. Thank you for the effort !

  6. #6 by zeflasher on September 19, 2007 - 10:47 am

    Thx for the comment, I appreciate. Some other tutorials are on their way but they will deal with pixlib's big brother: "lowRa" ( AS3 ). Stay tunned!

  7. #7 by long on November 24, 2007 - 9:52 am

    hi!
    thanks for the 3 tuts!
    is there no better way to know the total size? having to hard code each kb value in the xml seems a bit weird.
    just wondering...

  8. #8 by zeflasher on November 24, 2007 - 8:46 pm

    Hello long,

    Thx for the feedbacks

    AS2 has no way to get the size of the file prior to it's download.
    You can use server side scripting to retreive the filesize of the assets you want to dld though.
    I found putting the filesize in the xml the more conveniant way. But that's my way :)
    ++

(will not be published)