Preloading and Asset Management with Guttershark
I thought I’d share a quick article on how and why preloading, and asset management is so simple with guttershark.
The preload controller is designed to be the one access point for loading any type of files. As some of you might be snooping through the guttershark library - you’ll see that there are multiple classes that help do the work for the PreloadController, which are called “Workers”. Don’t be fooled by all the support classes - you don’t ever need to use them.
Basic example
Let’s look at an example, this only sets up the preloader to load in some assets, we’ll look at getting them out in a bit.:
package { import flash.events.Event; import net.guttershark.control.PreloadController; import net.guttershark.support.preloading.Asset; import net.guttershark.support.preloading.events.PreloadProgressEvent; public class TestPreloader extends Sprite { private var pc:PreloadController; private var assets:Array; public function TestPreloader() { pc = new PreloadController(); pc.addEventListener(Event.COMPLETE,onPreloadComplete); //when preloading all items complete. pc.addEventListener(PreloadProgessEvent.PROGRESS,onProgress); //progress for all items as a whole. //pc.addEventListener(AssetProgressEvent,onAssetProgress); //fired for every asset's individual progress. //**there are move events available. see docs for those.** //now let's add some assets assets=[]; assets.push( new Asset("assets/myPicture.jpg") ); assets.push( new Asset("assets/myOtherPicture.jpg") ); //add the assets pc.addItems(assets); //now let's kick off the preloader pc.start(); } private function onPreloadComplete(e:Event):void { trace("preloading complete"); } private function onProgress(ppe:PreloadProgressEvent):void { trace(ppe); } } }
What you’re looking at is the most basic form of preloading with guttershark. In the above example, we’re loading jpg’s. Internally to the Asset class, it uses a “BitmapWorker” to load in the asset. This is something you don’t have to specify, it will figure out what type of content your asset is based off of the file extension.
That raises a question - what about loading files that don’t have an extension? With asset instances, you can specify a “forced file type”, which informs the Asset of which worker to use. For example:
var rssAsset:Asset = new Asset("http://example.com/rss?feed=atom",null,"xml");
Ignore the second “null” parameter for now, I’ll get to that. The third parameter, is how you set a “forcedFileType”. So this asset will be loaded with the XMLWorker.
Showing progress
Guttershark has some cool conventions on multiple classes, which will automatically give you a pixel value based on some other type of value, such as progress, time, etc.
The PreloadProgressEvent has a property called “pixels”. This value is based off of what you tell the preloader to “fill”. For example, let’s say we wanted our preloader to fill 300 pixels. Keep in mind, what you do with this “pixel” value is up to you. It could be mapped to height, width, etc. Here’s the example (this is in addition to the above example):
package { /*..*/ public class TestPreloader extends Sprite { /*..*/ public var progress:MovieClip; public function TestPreloader() { pc = new PreloadController(300); //(this is key, it specifies the "pixelsToFill") /*..*/ } private function onPreloadComplete(e:Event):void { trace("preloading complete"); } private function onProgress(ppe:PreloadProgessEvent):void { /*..*/ progress.width=ppe.pixels; } } }
In the above example, the first argument to the preload controller constructor is the “pixelsToFill” property. Which is how the pixel value is delivered to you in the event handler.
There are a few other classes that offer this same type of behavior. The FLV class is another example - it will give you a “pixelsPlayed” property. Which you map to a progress bar. Read more of the guttershark docs for that.
Ok, that covers the majority of working with the preload controller. There are a few other methods, and properties that may be of use. You can check those out in the docs.
On to using the assets.
Accessing Assets with the AssetManager
Every preload controller you use stores all of the assets loaded in the AssetManager. The asset manager is a singleton - this creates a globally accessible asset library, so it doesn’t matter in which class, or in what scope assets are loaded - they all get into the asset manager.
The preload controller also (by default), loads all assets into the same application domain. You’ll see what that does in a bit.
Remember that “null” parameter for the Asset class from a few paragraphs ago? That parameter is called the “libraryName”. When you specify the library name for an asset, it registers that asset in the asset manager by that id.
So, to slightly alter the example so far, to attach a bitmap onto the stage. It would look like this (here’s the complete example):
package { import flash.events.Event; import net.guttershark.control.PreloadController; import net.guttershark.support.preloading.Asset; import net.guttershark.support.preloading.events.PreloadProgressEvent; public class TestPreloader extends Sprite { private var am:AssetManager; private var pc:PreloadController; private var assets:Array; public var progress:MovieClip; public function TestPreloader() { am = AssetManager.gi(); pc = new PreloadController(300); pc.addEventListener(Event.COMPLETE,onPreloadComplete); pc.addEventListener(PreloadProgessEvent.PROGRESS,onProgress); //now let's add some assets assets=[]; assets.push( new Asset("assets/myPicture.jpg", "myPicture" ) ); assets.push( new Asset("assets/myOtherPicture.jpg", "myOtherPicture") ); //add the assets pc.addItems(assets); //now let's kick off the preloader pc.start(); } private function onPreloadComplete(e:Event):void { trace("preloading complete"); addChild( am.getBitmap("myPicture") ); addChild( am.getBitmap("myOtherPicture") ); } private function onProgress(ppe:PreloadProgessEvent):void { trace(ppe); progress.width=ppe.width; } } }
Again, the important piece of code in this example, is the addition of the second parameter to the Asset instance. Each one has a library name, which will make that asset available in the asset manager.
For another example, let’s look at how you can load swfs, and grab assets out of the swf’s library, I’ve altered the asset that is loaded, and what happens when the preload is complete.:
package { /**/ public class TestPreloader extends Sprite { private var am:AssetManager; private var pc:PreloadController; private var assets:Array; public var progress:MovieClip; public function TestPreloader() { am = AssetManager.gi(); pc = new PreloadController(); /*..*/ //now let's add some assets assets=[]; assets.push( new Asset("assets/myAssetsSWF.swf", "assets" ) ); /*..*/ } private function onPreloadComplete(e:Event):void { trace("preloading complete"); addChild( am.getMovieClipFromSWFLibrary("assets", "myMovieClip") ); } /* .. */ } }
You should hopefully start to see some of the elegance with the preload controller, and asset manager.
How do application domains play into this picture? In the above example, I’m calling the method “getMovieClipFromSWFLibrary”. This method does like it sounds, it grabs an asset out of that swf’s application domain. There are other methods to get other assets as well, like getBitmapFromSWFLibrary, getSpriteFromSWFLibrary, etc.
That’s well and good, but we can take this further - because the preload controller loads everything into one application domain, technically you don’t have to tell the asset manager which swf that asset is in. So, we could alter this example like this:
package { /**/ public class TestPreloader extends Sprite { private var am:AssetManager; private var pc:PreloadController; private var assets:Array; public var progress:MovieClip; public function TestPreloader() { am = AssetManager.gi(); pc = new PreloadController(); /*..*/ //now let's add some assets assets=[]; assets.push( new Asset("assets/myAssetsSWF.swf", "assets" ) ); /*..*/ } private function onPreloadComplete(e:Event):void { trace("preloading complete"); addChild( am.getMovieClip("myMovieClip") ); } /* .. */ } }
Now you can see, that it doesn’t matter which swf the clip “myMovieClip” is defined in, because the swf is merged into one application domain.
This does raise one problem. Multiple library items, with the same export name, in different swfs. For example, if I have two swfs (swf1, swf2), and they both have the clip “GlossButton”. The last one to be loaded in will be discarded. If swf2 is loaded after swf1, the GlossButton in swf2 will be discarded. That’s the only pitfall to watch out for when using one application domain.
Conclusion
The PreloadController can load anything for you. It can load bitmap, swf, flv, xml, audio, and stylesheet (css) files.
Let me quickly note - manually creating Asset instances is a no-no! I never actually create Asset instances in code, the examples were to illustrate how the preload controller works.
So, in the next post, I’ll show you how to get rid of that annoying code that requires you to manually create Asset instances - we’ll do that with XML and the Model class.
And as always, read the docs.
2 Comments so far
Leave a reply
Great tutorial Aaron, Thanks. I can’t wait for the next one, I’m finally starting to make sense of the framework and I think its pretty great that you’re sharing this!
[...] on from Preloading and Asset Management. Let’s look at how the model can help with asset definitions, and removing hard coded [...]