Starling 2.4

Daniel Sperl on May 31, 2018

Summer is coming to the northern hemisphere, and it has become kind of a ritual to push out a new Starling release before it gets too hot, right?

The new release comes with one major addition that has been on my to-do list for a long time, but I never quite found the courage to tackle it. The AssetManager is actually a surprisingly complex beast that has to work some magic internally to get everything done the right way; making each step of the process customizable (as it should be) was a challenge!

The other news is an extension that gives you a great new option for importing animations from Adobe Animate CC. I think it turned out really well, and I hope to see it used in a lot of games soon! But let’s start at the beginning. :)

Processing Assets

A New AssetManager

Since its introduction in Starling 1.3, the AssetManager has always been an important part of Starling. AIR and Flash provide numerous ways to access data – from a simple embedded class to loading via URLLoader or a standard File instance. Furthermore, context loss mean that data sometimes needs to be restored from its original source – whatever that was.

The AssetManager has always taken care of all that, while simultaneously making sure that all kinds of assets can be accessed in the same way, no matter where they came from. It simply took away the need to care about much of those things, which is great.

However, internally, it was one of those classes that have grown over time, slowly turning into a pile of code that’s ugly and hard to extend. It works, yes, but you better not try to extend it or dig too deep into its internals.

That’s why I finally took the time to rebuild it from scratch! You can find the new version in the starling.assets package – while the old one (which is now deprecated) is still available in starling.utils. That way, you don’t have to update your code immediately – although I recommend doing it, as it’s really not much effort.

The new version has an interface that’s very similar to the old one, but is much easier to extend and contains a few features that have been requested for a long time. I’ll guide you through some of the new features below.

New ‘loadQueue’ callbacks

Before I’m getting to the more exciting new features, I need to tell you about the new callbacks for the loadQueue method. As you know, loadQueue is the method that actually works through the queue of assets you passed to enqueue. To indicate that it’s finished, this method always called a single onProgress callback that could be used for both a progress indicator, and to figure out when loading was complete (ratio being 1).

The new version splits that up into two callbacks and adds a third one for error handling. You use it like this:

assets.loadQueue(onComplete, onError, onProgress);

function onComplete():void { trace("done!"); }
function onError(error:String):void { trace("error:", error); }
function onProgress(ratio:Number):void { trace("progress:", ratio); }

You can always rely on onComplete being called exactly once at the end; onError and onProgress are optional, and they may be called multiple times. When upgrading to the new version, that’s the first change you should tackle.

Grouping Assets

Developers often told me they wanted to add assets from a specific folder, and then be able to remove those exact same assets (but not any other) later. My recommendation to those developers was always to use a separate AssetManager for those assets instead.

Well, my recommendation still stands – but it’s much easier to use. That’s because the new AssetManager supports nesting.

Let’s say you’ve got two folders called “game” and “menu”, containing textures for, well, the game and its menu. I’m loading those folders like this:

var manager:AssetManager = new AssetManager();
var appDir:File = File.applicationDirectory;

// game assets
var gameAssets:AssetManager = new AssetManager();
gameAssets.enqueue(appDir.resolvePath("textures/game/"));
manager.enqueueSingle(gameAssets, "gameAssets");

// menu assets
var menuAssets:AssetManager = new AssetManager();
menuAssets.enqueue(appDir.resolvePath("textures/menu/"));
manager.enqueueSingle(menuAssets, "menuAssets");

manager.loadQueue(onComplete);

As you can see, there is one main AssetManager here, simply called manager. Then there are two additional ones called gameAssets and menuAssets, to which we are enqueuing only the assets of the relevant folders. They are enqueued at the main manager – which will then take care of loading their assets.

Afterwards, those assets behave just as if they’d been added to manager directly, i.e. methods like getTexture or getSound will return assets from both sub-managers.

To remove the assets of one of the folders, you simply remove the respective sub-manager. (Ideally, disposing the assets along the way).

manager.removeAssetManager("menuAssets", true); // 2nd parameter means 'dispose'

Custom Asset Types

Of course, the new AssetManager still includes support for all the asset types needed in a typical app, like textures, sounds, JSON data, etc. However, now you can also add your own asset types – after all, every project is different.

An asset type is defined just like an event type: via a simple string. For example, you could define the String bitmapDataType to be used as identifier for assets of type BitmapData. To store bitmap data inside the AssetManager, simply pass that string to the addAsset and getAsset methods:

const assetType:String = "bitmapDataType";
var inData:BitmapData = new BitmapData(...);

// store data
assetManager.addAsset("myData", inData, assetType);

// retrieve data
var outData:BitmapData = assets.getAsset(assetType, "myData") as BitmapData;

To simplify this, you’d typically subclass AssetManager and add special access methods (like getBitmapData). That way, you don’t have to cast to BitmapData all the time.

However, that’s only half of the story. Because typically, you don’t add assets directly to the AssetManager, do you? Instead, you enqueue files or URLs and let the AssetManager do the adding itself.

But how can we teach the AssetManager to store bitmap data during that loading process?

Asset Factories

That’s what asset factories are for. One factory might by responsible for sounds, another for JSON data, and yet another for textures.

In our case, we could create a BitmapDataFactory that stores image files as bitmaps instead of textures. Or, even better, store the data both as bitmap and texture – this would come in handy if you need to look up the colors or transparency of certain pixels.

I don’t want to get too much into detail here, but you can easily see how it’s done by looking into the code of some of the standard factories – e.g. the JsonFactory or the SoundFactory. Owners of the Starling Handbook will find a new section on this topic, as well.

Once ready, you need to tell the AssetManager about your new factory:

assets.registerFactory(new BitmapDataFactory(), 1);

By registering the factory with a priority of 1 (final parameter), you make sure that it is preferred over the default factories (which all have a priority of zero).

Data Loaders

Actually, I just skipped a step: before any assets can be instantiated, they are typically loaded from somewhere. That means that a stream of bytes is flowing either from a server or from a local file. At the end of this process, we’ve got the raw ByteArray that makes up the asset.

That’s another step that can now be customized. This time, you’re going to extend the new DataLoader class.

DataLoader is basically just an alternative to URLLoader that’s easier to use because it relies on simple callbacks instead of the bulky event system of the original.

You can use it entirely without the AssetManager, if you want.

var loader:DataLoader = new DataLoader();
loader.load("http://my.server/data.bin", 
    function(data:ByteArray):void
    {
        trace("received " + data.length + " bytes");
    });

(Just like the AssetManager’s loadQueue method, load also contains optional callbacks for error and progress handling.)

Here’s where it gets interesting: you can let the AssetManager do all the loading with your own, custom DataLoader subclass. That way, you could e.g. easily add support for zipped assets or make sure remote assets are cached on disk for faster access in subsequent sessions.

As luck would have it, I already implemented both of these ideas in new extensions:

Adobe Animate Extension

As you all know, the easiest way to display an animation (like, say, the run cycle of your hero) in Starling is via a MovieClip. You’d use e.g. Adobe Animate to design and animate your character, export a sprite sheet (texture atlas, in Starling terminology) containing all the individual frames and feed it to a MovieClip.

That’s really straight-forward, but the approach is limited regarding the size and length of animations. There are situations where you might run out of graphics memory.

For this reason, tools like Dragon Bones or Spine allow a more efficient approach. They split a character up into different parts and animate each part separately – which requires much less texture memory.

Fairly recently, Animate CC received a new export feature that does just the same, without the need of a plugin. To use it, right-click on a symbol in the Library panel and choose “Generate Texture Atlas”. Don’t be fooled by the misleading name: this format has nothing to do with Starling’s standard texture atlases. It’s a completely different format that efficiently describes animations.

Up until now, this feature doesn’t seem to have been picked up by many game engines; the bad naming and lack of documentation have probably hindered a wider adoption. However, I think it has great potential, so I created a clean and easy-to-use extension for Starling that parses the new format and displays these animations in Starling.

All the details about this extension can be found in the Starling Wiki:
Adobe Animate Extension

The great “Ninja Girl” animation alone makes it worth checking out! Chris Georgenes kindly created this animation to demo the new extension.

And some more …

As usual, the new version contains several bug fixes and small optimizations, too – it’s always worth the upgrade! Here are some notable changes:

  • added support for 8k textures
  • added support for the Context3D option ‘wantsBestResolutionOnBrowserZoom’ via ‘Starling.supportBrowserZoom’
  • added workaround for ADL mouse problem on Microsoft Surface Books
  • added ENHANCED stage3D profile to ‘auto’ profile list
  • reduced the number of allocations in AOT mode (iOS)
  • fixed Animate CC sprite sheet support by duplicating pivot points across textures with the same prefix
  • fixed that filter and mask on ‘this’ were ignored by ‘DisplayObject.drawToBitmapData’

A huge thanks to all the contributors of this release, be it via forum posts, GitHub reports or direct e-mails. You rock!

Now, head over to the download section and give the new version a try. And don’t forget to add a comment below, I’m looking forward to your feedback!