Asset Management

One thing should be clear by now: textures make up a big part of every application’s resources. Especially when we are talking about games, there is a lot to manage; from the user interface to the characters, items, backgrounds, etc. But that’s not all: you will probably need to deal with sounds and configuration files, too.

For referencing these assets, you’ve got several choices.

  • Add them to the Lime project.xml file (via the <assets> tag).

  • Load them from disk (only possible for native applications).

  • Load them from an URL, e.g. from a webserver.

Since every option requires different code (depending on the asset type and loading mechanism), it’s difficult to access the assets in a uniform way. Thankfully, Starling contains a class that helps you with that: the AssetManager.

It supports the following types of assets:

  • Textures (either from Bitmaps or ATF data)

  • Texture atlases

  • Bitmap Fonts

  • Sounds

  • XML data

  • JSON data

  • ByteArrays

To accomplish this, the AssetManager uses a three-step approach:

  1. You add pointers to your assets to a queue, e.g. File objects or URLs.

  2. You tell the AssetManager to process the queue.

  3. As soon as the queue finishes processing, you can access all assets with corresponding get methods.

The AssetManager contains a verbose property. If enabled, all steps of the enqueuing and loading process will be traced to the console. That’s very useful for debugging, or if you don’t understand why a certain asset is not showing up! For that reason, the latest Starling versions have it enabled per default.

AssetManager versions

Starling 2.4 features a new, much enhanced AssetManager. All the code examples that follow were written with this new version in mind.

The new AssetManager class can be found in the starling.assets package. For compatibility reasons, the old version is still available at its previous location (in starling.utils), but it’s now marked as deprecated. Upgrading to the new version should be really painless — the changes in the public API were kept at a minimum. In exchange, you can profit from the new version’s much higher flexibility.

Enqueuing the Assets

The first step is to enqueue all the assets you want to use. How that’s done exactly depends on the type and origin of each asset.

Assets from disk or from the network

Enqueuing files from disk or from a remote server is rather straight-forward:

// Enqueue an asset from a remote URL
assets.enqueue("https://example.com/img/starling.jpg");

// Enqueue an asset from disk (native targets only)
var appDir:File = File.applicationDirectory;
assets.enqueue(appDir.resolvePath("sounds/music.mp3"));

// Enqueue all contents of a directory, recursively (native targets only).
assets.enqueue(appDir.resolvePath("textures"));

To load a texture atlas, just enqueue both its XML file and the corresponding texture. Make sure that the imagePath attribute in the XML file contains the correct filename, because that’s what the AssetManager will look for when it creates the atlas later.

assets.enqueue(appDir.resolvePath("textures/atlas.xml"));
assets.enqueue(appDir.resolvePath("textures/atlas.png"));

Bitmap Fonts work just the same. In this case, you need to make sure that the file attribute in the XML (the .fnt file) is set up correctly.

assets.enqueue(appDir.resolvePath("fonts/desyrel.fnt"));
assets.enqueue(appDir.resolvePath("fonts/desyrel.png"));

Per-Asset Configuration

When you create a texture manually (via the Texture.from…​() factory methods), you’ve got a chance to fine-tune how it is created. For example, you can decide on a texture format or scale factor.

The problem with those settings: once the texture is created, you cannot change them any more. So you need to make sure the correct settings are applied right when the texture is created. The asset manager supports this kind of configuration, too:

var assets:AssetManager = new AssetManager();
assets.textureOptions.format = Context3DTextureFormat.BGRA_PACKED;
assets.textureOptions.scale = 2;
assets.enqueue(EmbeddedAssets);

The asset manager will adhere to these settings for all the textures it creates. However, it seems that this would only allow one set of properties for all the loaded textures, right? Actually, no: you just need to enqueue them in several steps, assigning the right settings prior to each call to enqueue.

assets.textureOptions.scale = 1;
assets.enqueue(appDir.resolvePath("textures/1x"));

assets.textureOptions.scale = 2;
assets.enqueue(appDir.resolvePath("textures/2x"));

This will make the textures from the 1x and 2x folders use scale factors of one and two, respectively.

Loading the Assets

Now that the assets are enqueued, you can load all of them at once. Depending on the number and size of assets you are loading, this can take a while.

The loadQueue method accepts up to three different parameters, with separate callbacks for completion, error handling and progress. Only the first parameter (onComplete) is obligatory.

assets.loadQueue(onComplete, onError, onProgress);

function onComplete():Void { trace("done!"); } (1)
function onError(error:String):Void { trace("error:", error); } (2)
function onProgress(ratio:Float):Void { trace("progress:", ratio); } (3)
1 You can always rely on onComplete to be called once at the very end.
2 onError may be executed multiple times (once for every asset that can’t be loaded).
3 Use onProgress if you want to show some kind of progress bar or loading indicator.

That’s the one part where you need to be careful when migrating to the new AssetManager. Previously, there was just a single callback parameter in the loadQueue method that signaled both progress and completion. Make sure to update your call so that it uses the new separate callbacks.

With an enabled verbose property, you’ll see the names with which the assets can be accessed.

[AssetManager] Adding sound 'explosion'
[AssetManager] Adding texture 'bird'
[AssetManager] Adding texture 'atlas'
[AssetManager] Adding texture atlas 'atlas'

Accessing the Assets

Finally: now that the queue finished processing, you can access your assets with the various get…​ methods of the AssetManager. Each asset is referenced by a name, which is the file name of the asset (without extension) or the class name of embedded objects.

var texture:Texture = assets.getTexture("bird"); (1)
var textures:Vector<Texture> = assets.getTextures("animation"); (2)
var explosion:SoundChannel = assets.playSound("explosion"); (3)
1 This will first search named textures, then atlases.
2 Same as above, but returns all (sub) textures starting with the given String.
3 Plays a sound and returns the SoundChannel that controls it.

If you enqueued a bitmap font along the way, it will already be registered and ready to use.

In my games, I typically store a reference to the asset manager at my root class, accessible through a static property. That makes it super easy to access my assets from anywhere in the game, simply by calling Game.assets.get…​() (assuming the root class is called Game).

results matching ""

    No results matching ""