AS3: Load assets dynamically for better performance

Asset management is a crucial point of every game. These assets may include bitmap images, audio and video files, animations, 3D models etc. etc. Depending on the size of the game there might be hundreds of such assets. How these assets and handled can have severe impact on the performance and user experience. One way to handle the assets is to embed all the assets with the main swf file. The obvious result is a big main swf file which will take more time to load and user will need to wait more. This leads to a very bad user experience for medium size games and just impossible for big size games. Better way is to embed only the required assets to start the game and load the remaining things after the game starts or load them only when that is needed. This requires dynamic loading of assets, the thing that I am going to discuss here.

In this tutorial I am going to create a simple asset file which contains 3 bitmap images, load that asset file dynamically and then load the bitmaps from the asset file. I have assumed that the reader is already familiar with the AS3 language and flash itself. That means I won’t explain what is a class, method or what is a swf or fla file.

Prepare the project and asset file

  • Create a new AS project named LoaderDemo. I am using Adobe Flash Builder 4 for this. Make sure that Copy non-embed files to output folder is checked under project Properties/ActionScript Compiler (by default it is checked).
  • Create a folder named Asset under the project directory. The fla file will be stored here. Note that this is not under src directory. Only the published swf file is needed in the output directory, so there is no need to store fla file under src directory.
  • Create a fla file under Asset folder. Create three rectangle images of size 100×50 and fill them with color red, green and blue by using any image manipulation software like photoshop or paint. These are the bitmap images that will be loaded from the asset file. Drag and drop the files to fla’s Library panel. The bitmaps will be copied to the fla.
  • Now the images are stored in the fla. An way to access them after the file is loaded is required. Right click on the blue.png and select Properties. Check Export for ActionScript and edit the Class to BoxBlue. The base class is BitmapData.
  • Same thing is done for the other two images. Now the linkage for all bitmaps are established.
  • Publish the file. If there is a warning something like new classes will be generated then select Yes. This will create the asset.swf file which contains the bitmaps.
  • Create asset folder under src and copy the swf to src/asset. After building the project this swf will be available in output folder.
  • And we are done with the asset preparation.

Load the swf dynamically

  • Now comes the coding part. We want to load the asset after the main swf file is loaded. Add a listener for the ADDED_TO_STAGE event in the constructor of main LoaderDemo class. The swf will be loaded from this listener function.
    package
    {
        import flash.display.Sprite;
        import flash.events.Event;
        
        public class LoaderDemo extends Sprite
        {
            public function LoaderDemo()
            {
                addEventListener(Event.ADDED_TO_STAGE, mainSWFLoaded);
            }
            
            private function mainSWFLoaded(evt:Event):void {            
            }
        }
    }
    
  • Create a class named AssetManager. This will handle the loading of the asset. This class contains an instance of AS Loader class which is used to load asset files, a callback function which will be fired after the loading is completed and the relative path to asset file. The loader is instantiated in the constructor.
    package
    {
        import flash.display.Loader;
    
        public class AssetManager
        {
            private var assetLoader:Loader = null;
            private var loadingCompletedListener:Function = null;
            
            private const ASSET_FILE_PATH:String = "asset/asset.swf";
            
            public function AssetManager()
            {
                assetLoader = new Loader();
            }
        }
    }
    
  • Create a method loadAsset in AssetManager. Loading of the asset swf file will be started when this method is called. It takes a parameter listener which is the callback function that should be fired when the loading is complete. The client of the AssetManager (main class in this example) supplies this function. The Loader class have a property contentLoaderInfo which is an instance of AS LoaderInfo class which can provide various information about the loaded file and also handles the loading related events. We are particularly interested in COMPLETE event. So an event listener loadingComplete for this event is added. From the event listener the callback which was supplied by the client is simply fired so the client is informed that loading is complete. After registering the event actual loading of the asset is started by calling load method of the assetLoader. The parameter of the load is a URLRequest object.
    public function loadAsset(listener:Function):void {
        this.loadingCompletedListener = listener;
        
        assetLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadingComplete);
        assetLoader.load(new URLRequest(ASSET_FILE_PATH));
    }
    
    private function loadingComplete(evt:Event):void {
        loadingCompletedListener();
    }
    
  • Add an instance of AssetManager in LoaderDemo. Instantiate this in mainSWFLoaded and call loadAsset to start the loading.
    private var assetManager:AssetManager = null;
    
    private function mainSWFLoaded(evt:Event):void {
        assetManager = new AssetManager();
        assetManager.loadAsset(assetLoaded);
    }
    
    private function assetLoaded():void {
        trace("asset loading complete");
    }
    

    The trace is printed when the loading is complete.

  • Now the swf containing the bitmaps is loaded. We need to access the bitmaps from it. During the asset preparation we linked the red, green and blue bitmap files against the class definition BoxRed, BoxGreen and BoxBlue respectively, which all are subclass of BitmapData. The first thing that is required is to get the class definition from the loaded swf. After getting the class definition we can instantiate them.

    The contentLoaderInfo property (which is an instance of LoaderInfo) of assetLoader contains a property named applicationDomain, an instance of AS class ApplicationDomain which is a container of class definitions. All the classes that are defined in the swf file can be accessed by using methods of applicationDomain. So, define a method getClassDefinition in AssetManager which takes a definition string as parameter and check whether the class is present in the swf. If present then that class definition is returned as Class object.

    public function getClassDefinition(definition:String):Class {
        if (assetLoader.contentLoaderInfo.applicationDomain.hasDefinition(definition)) {
            return assetLoader.contentLoaderInfo.applicationDomain.getDefinition(definition) as Class;
        }
        
        return null;
    }
    
  • Define a method createBitmap in LoaderDemo which takes a class name as string and returns the Bitmap if the class is present in the swf. It first gets the Class object from assetManager by calling getClassDefinition. The returned class object is a subclass of BitmapData. Then a new object is instantiated from the returned class object. The constructor of BitmapData takes two parameter, width and height. In this case that is set correctly from the asset, even then we need to pass 0, 0 for them. And then a Bitmap is created from the data and returned.
    private function createBitmap(bmpClassName:String):Bitmap {
        var bmpClass:Class = assetManager.getClassDefinition(bmpClassName);
        
        if (bmpClass) {
            var bmpData:BitmapData = new bmpClass(0, 0) as BitmapData;
            return new Bitmap(bmpData);
        }
        
        return null;
    }
    
  • We are done. The only remaining thing is to test the code. So we create three bitmaps in assetLoaded and add them as the child.
    private function assetLoaded():void {
        trace("asset loading complete");
        
        var bmp1:Bitmap = createBitmap("BoxRed");
        addChild(bmp1);
        
        var bmp2:Bitmap = createBitmap("BoxGreen");
        bmp2.y = 100;
        addChild(bmp2);
        
        var bmp3:Bitmap = createBitmap("BoxBlue");
        bmp3.y = 200;
        addChild(bmp3);
    }
    
  • After running the project, here is the output.

Few final notes

  • This example handles only one asset file. In real situation more than one asset file might be required to load dynamically.
  • assetManager is a member of LoaderDemo. A Singleton might be a better choice.
  • createBitmap is a method of LoaderDemo. This might be okay for a small tutorial like this. But in reality a Factory should be used.
  • Bitmaps are not handled here efficiently. createBitmap creates a new bitmap every time it is called. However, there should be no reason to load the same bitmap data more than once in memory. Bitmap data can be shared efficiently by using a Flyweight.
  • The asset file path is hardcoded in this example. However, it is better to read them from a config file.
  • Though this example contains only bitmap assets, the same technique can be used for other types of assets.
  • AS3 reference manual contains detail information on Loader, URLRequest, LoaderInfo and ApplicationDomain classes.
  • A nice tutorial on loader can be found here.
  • There are other methods to manage assets. They can be found here.

The full code for this tutorial can be downloaded from here. Any feedback is welcome.

Advertisements
This entry was posted in AS3 and tagged . Bookmark the permalink.

5 Responses to AS3: Load assets dynamically for better performance

  1. Andrea says:

    This is great, thanks for sharing

  2. Romuhica says:

    Hi !

    First, this tutorial is really great, and thaks for sharing. But now, I have just one issue. I want to load movieClip instances in my project. But the problem is that when I test the code, it lauch a Error #2007 : Parameter child must be non-null.

    I follow your tutorial step by step, and i don’t understand why my MC can’t be load and added to stage. Here the code for the createBitmap function (I call it createAnimation) :

    private function createAnimation(swfClassName:String):MovieClip
    {
    var mcClass:Class = _assetManager.getClassDefinition(swfClassName);
    if(mcClass)
    {
    var mc:MovieClip = new mcClass() as MovieClip;
    }
    return null;
    }

    And the assetLoaded function :

    private function assetLoaded():void
    {
    trace(‘asset loading complete’);

    var _storm:MovieClip = this.createAnimation(‘storm’);
    addChild(_storm);
    }

    All my swf in the asset.swf are set to be exported in AS3. Can you help me ?

    Romuhica

  3. And about Font file (.ttf)? Is it possible load it from source?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s