Table of Contents

Starling 2 - Migration Guide

The majority of the changes in Starling 2 are happening completely behind the scenes; for many projects, it should be very easy to upgrade to the new version.

There are two exceptions, though:

In such cases, it might be better to just stick with the old version for the time being. I will continue to support it with bugfixes, so there's no immediate need to upgrade. Never change a running system, right?

For any new projects, though, I highly recommend switching to Starling 2.

This document gives long-time Starling users a quick overview about some gotchas they might encounter during the migration. My recommendation: check in (or save away) your current project version (just to be sure), then replace the old Starling SWC with the new one. After that, it's just a matter of fixing all the errors and warnings your compiler will throw at you. :-)

TextField

TextFormat

The TextField is finally accompanied by a TextFormat class. The setup is quite similar to the classic Flash classes — but without the pain of constantly having to re-assign the format for changes to show up. In Starling, when you change the TextField's format, it's going to be updated right away!

Furthermore, the HAlign and VAlign classes were merged into one Align class that contains constants for both directions.

// old version:
textField = new TextField(width, height, "Lorem Ipsum", "Arial", 12);
textField.hAlign = HAlign.LEFT;
 
// new version:
textField = new TextField(width, height, "Lorem Ipsum");
textField.format.setTo("Arial", 12);
textField.format.horizontalAlign = Align.LEFT;

Native Filters

The old nativeFilters property on the TextField class is gone for good, I'm afraid. That property was introduced before Starling had filters, and I figured that my time would be better spent enhancing the filter API than porting this over. (Using those native filters was actually not as straight-forward as one would assume, because they might change the bounds of the text area.)

My recommendation: use the new filters that are coming with Starling. For best performance, call filter.cache() right away. That will yield the same performance as before! Call cache() again when the text changes.

Button

Since the Button class contains an optional TextField as well, it made sense to add such a format property to it, too. It's called textFormat.

// old version:
var button:Button = new Button(texture);
button.fontSize = 20;
button.fontName = "Arial";
 
// new version:
var button:Button = new Button(texture);
button.textFormat.size = 20;
button.textFormat.font = "Arial";

ClipRect property

In Starling 1.x, you could use the clipRect property on sprites to trim the rendered area to the bounds of a stage-aligned rectangle. This property was removed altogether; instead, just use the mask property (introduced in Starling 1.7) with a Quad.

var sprite:Sprite = new Sprite();
 
// old version:
sprite.clipRect = new Rectangle(10, 10, 50, 50);
 
// new version:
sprite.mask = new Quad(50, 50);
sprite.mask.x = sprite.mask.y = 10;

Different to the old property, that works on all display objects (not just sprites) and even supports rotations. Performance-wise, it's just as fast: Starling automatically recognizes that the mask has a rectangular shape and uses Stage3D's scissor rectangle, just like the “clipRect” did in previous versions.

Texture repeat

The repeat property on the Texture class is no more. The problem with that concept was that you're working with atlas textures most of the time, and there's no way to get them to repeat correctly.

However, there's now a much more powerful replacement: the tileGrid property on the Image class. Feathers users will find this familiar: it mimics / replaces Feather's TiledImage.

If you assign a Rectangle to this property, the image will be divided into a grid that displays the current texture in each and every cell. The assigned rectangle points to the bounds of one cell; all other elements will be calculated accordingly. A zero or negative value for the rectangle's width or height will be replaced with the actual texture size. Thus, you can make a 2×2 grid simply like this:

var image:Image = new Image(texture);
image.tileGrid = new Rectangle();
image.scale = 2;

Alternatively, there's also a textureRepeat property on the Image class now that does work just like the old repeat. However, it doesn't work for all kinds of textures, so you're generally better off with the tileGrid.

No more "flatten"

Starling 2 introduces a render cache, which automatically recognizes all objects that have not been modified since the last frame and draws them from the cache. This is extremely efficient and takes a huge burden off the CPU, especially for business-apps or menu screens that contain mostly static objects.

Thus, the usefulness for the old “flatten” feature was drastically decreased; especially since performance critical scenarios are better solved by using the new MeshBatch class directly (which replaces the old QuadBatch). It was also often misunderstood for a cacheAsBitmap replacement, and thus used in cases that actually hurt performance.

The feature might come back one day — but for now, just remove all those calls.

Changes in the Juggler

You might be aware that some of the juggler's methods use object pooling internally. For example, when you call juggler.tween() or juggler.delayCall(), the methods will take the Tween and DelayedCall instances from a pool for maximum efficiency.

However, they previously also returned instances to those objects, even though they were disguised as instances of IAnimatable. The problem: if anybody kept those instances and used them after the juggler was already finished with them, they might point to objects already used elsewhere! This lead to bugs that were extremely hard to track down.

For this reason, the Juggler now returns uint handles instead. They are unique and can be used to remove objects from the juggler via removeByID, avoiding any of those side effects.

// old version:
var tween:IAnimatable = juggler.tween(hero, 2, { x: 100 });
Starling.juggler.remove(tween);
 
// new version:
var tweenID:uint = juggler.tween(hero, 2, { x: 100 });
juggler.removeByID(tweenID);

VertexData

The VertexData class was completely rewritten and is now much more flexible than before. The new version can store arbitrary data, making it one of the fundamental building blocks of Starling's new rendering architecture.

You can almost use it like before, though, if you just modify your method calls a little.

var position:Point = new Point();
var texCoords:Point = new Point();
 
// old version:
vertexData.getPosition(0, position);
vertexData.setPosition(0, 320, 200);
vertexData.getTexCoords(0, texCoords);
vertexData.setTexCoords(0, 0.5, 0.5);
 
// new version:
vertexData.getPoint(0, "position", position);
vertexData.setPoint(0, "position", 320, 200);
vertexData.getPoint(0, "texCoords", texCoords);
vertexData.setPoint(0, "texCoords", 0.5, 0.5);

Removed Vector- and Array-Util

Those classes had a very short life-time, being introduced only in Starling 1.7. With AIR 19, Adobe added the insertAt and removeAt methods directly to the Vector- and Array-classes, making those helper classes obsolete.

Be careful, though, if you made use of negative indices with Starling's implementations, e.g.

ArrayUtil.insertAt(array, "test", -1);

Starling's code would insert “test” at the end of the array; Adobe's code at the next-to-last index. Doesn't make any sense IMHO, but well … that's another story. Just keep the difference in mind.

QuadBatch → MeshBatch

The QuadBatch is no more. The replacement is the new MeshBatch class, which works with all types of meshes, which includes Quad and Image, of course. Usage is almost identical.

Moved or renamed Methods

One of the first things I did when I started with the new version was a clean-up of the “utils”-package. Thus, some of the functions now moved into container classes.

utils.formatString() => utils.StringUtil.format()
utils.cleanMasterString() => utils.StringUtil.cleanMasterString()
utils.getNextPowerOfTwo() => utils.MathUtil.getNextPowerOfTwo()

Furthermore, some properties and methods were renamed slightly, for better consistency or just because I felt like doing so, hehe. ;-)

image.smoothing => image.textureSmoothing

Skipping unchanged Frames

There's one very useful new feature in Starling that I want to mention here, because I think it's relevant for anyone upgrading to the new version. That's the new property skipUnchangedFrames on the Starling instance. If enabled, Starling will recognize if the stage hasn't changed since the last frame and won't do any rendering in that case. This can potentially save the users of your app a LOT of battery time!

The only side effect: if you're using RenderTexture or VideoTexture instances, you have to take a little special care (you need to call starling.setRequiresRedraw() when their contents is changed). That's also the reason why I didn't activate the feature by default.

An easy way to see if you're benefiting from this change: enable the statsDisplay and have a look at its color: whenever it turns from black to dark green, the majority of the last couple of frames has been skipped.

KeyboardEvents are no longer broadcast

Previously, you could add KeyboardEvent.KEY_DOWN/KEY_UP events to any display object: those events were broadcast to all objects on the stage. However, that lead to performance issues if people had lots of objects on the screen.

For this reason, I'm now dispatching them only to the stage. The easiest way to listen to those events now is like this:

Starling.current.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);

It's very important that this event handler is removed when the current object is removed from the display list. Thus, I actually recommend doing it the following way:

// in the class constructor:
stage.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
stage.addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
 
// instance methods
private function onAddedToStage():void
{
    stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
 
private function onRemovedFromStage():void
{
    stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}

That way, it can't happen that the object still listens to those events, even if it has been removed from the stage. That will avoid any memory leaks, too.

Misc

Here are some more changes that didn't quite fit into their own category.

Those should be the most important gotchas! If anyone finds something worth mentioning that I forgot, feel free to add that below. Thanks in advance!

Room for other notable differences.