7. Extensions

To this point, we looked at all kinds of features that are part of the actual Starling library. Of course, these features cannot cover all possible use cases, though. It would be impossible to provide everything out of the box; every game or app has different needs and requirements, after all.

For this reason, Starling does not even attempt to be a jack-of-all-trades. Its goal has always been to provide a lightweight, robust core that can be extended in lots of different ways. Especially since the introduction of Starling 2.0, I think it has come very close to that goal. Thankfully, other developers quickly embraced this offer and made good use of Starling’s extensibility!

Extensions Wiki

The Starling Wiki provides a respectable list of extensions of all types. Find it here: Starling Extensions.

If you ever decide to write your own extension (thanks in advance!), please take the extra effort to add it to this list. That way, it will be easy for other developers to find it, and you can add some form of documentation or sample code right away.

An extension doesn’t have to be big and complex, by the way! If it consists of just a few lines of code that solve a common problem in a smart way, that’s already reason enough to share it with the community.

I even wrote some of the extensions myself, providing features that I’m considering useful, but just not universal enough to be part of the main framework. I provide the same support for them as for the rest of the Starling code.

In this chapter, we will look at some of the most useful extensions, tools and libraries:

Particle System

Create special effects like explosions, smoke, snow, fire, etc.

Dynamic Lighting

Use normal maps for realistic three-dimensional lighting effects.

Texture Mask

Mask a display object based on the alpha values of a texture.

The most exciting additions are saved for last: they are hugely popular among Starling users and are extremely extensive!

Feathers

Provides an incredible number of user interface components to spice up your games or power complex business apps.

Starling Builder

A visual editor allowing to create in-game user interfaces in a matter of minutes.

Let’s jump right in!

7.1. Particle System

Particle systems can be used to create special effects like explosions, smoke, snow, fire, etc. To provide that flexibility, they can be configured with numerous settings that control particle movement, size, color, etc.

This was actually the very first extension I created for Starling. What’s so nice about particle systems is that you can achieve fascinating results really quickly. The first demos of Starling always included some particle system shenanigans.

Finding the perfect settings for a particle effect is not easy. For that reason, there are tools that allow you to modify the settings and preview the resulting particle system in real time.

Particle Editor

A free online tool from onebyonedesign, built right with Starling! To try it out, just head to onebyonedesign.com.

Particle Designer

The original from 71squared, coming with a large online library of sample emitters. Warmly recommended for any macOS user! Get it on 71squared.com.

Particle Designer
Figure 107. The Particle Designer from 71squared.

Each tool allows you to export a texture and a .pex file, the latter containing the settings of the particle system. Let the AssetManager take care of this data. The extension itself provides a display object that is set up with these resources.

var psConfig:XML = assets.getXml("particle-system");
var psTexture:Texture = assets.getTexture("particle");

var ps:PDParticleSystem = new PDParticleSystem(psConfig, psTexture); (1)
ps.x = 160;
ps.y = 240;

addChild(ps); (2)
Starling.juggler.add(ps);

ps.emitterX = 20; (3)
ps.emitterY = 40;

ps.start(); (4)
ps.stop();
1 Create the particle system and move it to the desired location.
2 As any animated display object, it needs to be added to both display list and juggler.
3 The emitter coordinates define where the particles are emitted.
4 The methods start and stop turn the emitter on and off, respectively.

7.2. Dynamic Lighting

The task of this extension is nothing less than providing Starling with a realistic lighting model. The results are rather fascinating for a humble 2D engine, if I may say so.

To use it, you first need to add one or more light sources to the display list. You can choose between different types of lights.

Ambient lights

Add a basic illumination to all parts of the mesh, independent of its location or orientation.

Point lights

Emit radial light from a single spot, like a candle or light bulb.

Directional lights

Emit parallel rays, which is ideal to simulate sunlight.

For example, the following code creates a red point light.

var pointLight:LightSource =
    LightSource.createPointLight(Color.RED);

pointLight.x = 200;
pointLight.y = 150;
pointLight.z = -100;
addChild(pointLight);

Note that light sources inherit from the Sprite3D class, which means that they may be placed in a 3D space (even though the rest of your scene is typically 2D). Which parents you are adding them to is entirely up to you; the lights can be anywhere in the display list.

At development time, it’s often useful to have a visual representation of the light. To get one, just enable the showLightBulb property:

pointLight.showLightBulb = true;

You can drag the light symbol around on the screen; hit Shift while moving the mouse to change rotation (useful for directional light) and z-position.

The mechanism that’s responsible for calculating the actual illumination is, of course, a mesh style. Create an instance of the LightStyle class and assign it to an Image (or any other mesh type). To configure how exactly the object will appear when hit by light, the style provides a number of properties.

var image:Image = getImage();
var style:LightStyle = new LightStyle();
style.ambientRatio = 0.3;
style.diffuseRatio = 0.7;
style.specularRatio = 0.4;
style.shininess = 8.0;
image.style = style;

The properties have the following meaning:

ambientRatio

The amount of ambient light reflected by the surface. That’s the basic illumination of a scene; even if a surface is not hit by light rays, it will receive some ambient light. [Range: 0-1]

diffuseRatio

The amount of diffuse light reflected by the surface. Illumination is determined by the angle with which the light hits the surface. [Range: 0-1]

specularRatio

The amount of specular light reflected by the surface. That’s basically the reflection of the light source on the surface. [Range: 0-1]

shininess

This affects the specular reflections on the surface. High values will yield small, point-like specular spots, like you would expect from a mirror. [Range: 0-32]

This way of calculating illumination is called the Phong Reflection Model. The image below shows you how the different components add up to the final color of each pixel.

Phong Lighting
Figure 108. The Phong Reflection Model.

Up to this point, the illumination doesn’t look all too exciting, though — after all, the light is shining on a perfectly flat object. To change this, we need to add some depth to the object!

That’s done by setting up a normal vector for each pixel of the object. Technically, the object will still be flat — but the light will bounce off just as if it were truly three dimensional!

Question is, how to get to those vectors? My recommendation: use the excellent SpriteIlluminator from Andreas Löw (also the author of TexturePacker). It turns normal map creation into a simple process that’s no more difficult than drawing with colors. You can get it from CodeAndWeb.

Create the normal map in the same dimensions as the standard texture and then pass it to the LightStyle constructor. That will do it!

var normalMap:Texture = assets.getTexture("normal-map");
var style:LightStyle = new LightStyle(normalMap);
Dynamic Lighting
Figure 109. A normal map (far right) makes the character truly stand out. Note the three light sources at the top.

7.3. Texture Mask

As you already know, the mask property is Starling’s way of providing stencil masks for display objects. Typically, a mask is a Canvas object that contains a simple vector shape.

One problem with stencil masks is that they do not take any texture data into account; only the shape of the polygon counts. That makes it difficult to create masks with shapes that are more complex than the primitives provided by the Canvas class. This class solves this problem: it allows you to use a texture’s alpha channel as a mask!

The extension is implemented as a simple mesh style. Its task is rather morbid: kill every pixel with an alpha value below a certain threshold.

The AGAL opcode for this operation is literally kil.

This does not simply mean that the pixels will be rendered with an alpha value of zero — it means that they will not be rendered at all. The result is that the stencil buffer won’t be modified, either; and that’s the whole idea behind this style.

var maskTexture:Texture = assets.getTexture("mask"); (1)
var mask:Image = new Image(maskTexture);
var maskStyle:TextureMaskStyle = new TextureMaskStyle();
mask.style = maskStyle;

var sprite:Sprite = createSprite(); (2)
sprite.mask = mask;
addChild(sprite);

style.threshold = 0.3; (3)
1 The actual mask is an image that uses the TextureMaskStyle to display the mask texture.
2 This sprite contains the contents that we want to be masked. We assign our new mask to the respective property.
3 Change the threshold to fine-tune the result; any alpha value below this number will be masked.

If you have problems understanding what this style really does, try displaying the mask image directly (without assigning it to sprite.mask). You will see that all pixels with alpha values below the given threshold will vanish completely.

Texture Mask
Figure 110. Here, the threshold of a texture mask is animated over time.

7.4. Feathers

This introduction was written by Josh Tynjala, the creator of Feathers.

7.4.1. What is Feathers?

Feathers provides a large library of user interface components for Starling Framework. Whether you’re building a game or an application, you can choose to use only a few Feathers components or all of them. Robust skinning APIs allow you to change the appearance of Feathers components to match the exact style that you have in mind. It doesn’t matter if Feathers is running on mobile or desktop, it was designed to work anywhere.

Feathers Themes

Feathers includes a number of example themes that will automatically skin every component with a consistent style. These themes can be a good starting point for a new project, or you can create your own skins from scratch to match your custom designs. You can skin any Feathers component to use different font styles, different background or icon textures, or different layout options like padding and alignment.

Feathers is designed with performance in mind, and all UI components are optimized to run at smooth framerates — whether you’re running your project on a desktop computer or a mobile device. Containers can scroll with a drag gesture on touchscreens, or you can display fixed scroll bars that may be used with a mouse for a desktop app. Feathers even provides an optional scaling system that automatically adjusts Starling’s view port and stage dimensions to create a consistent layout across devices.

Who should use Feathers?

The Feathers architecture was designed with two types of developers in mind — game developers and application developers. You can pick and choose a small number of components that you actually need to enhance your Starling game, but you can also build an entire application by taking advantage of all the layouts, navigation, and other advanced features that Feathers provides.

  • A game developer might drop in a Feathers Slider component to control the music volume, and a Feathers List component might be perfect to display the game’s high scores. Feathers components are regular Starling display objects, so you can insert them anywhere in your game with a simple call to addChild().

  • An application developer might use more advanced components for navigation and layout. For instance, a Drawers component may be used to add a navigation drawer that links to multiple sections of a mobile app, and the StackScreenNavigator component will keep track of history so that users can go back to a previous screen. A List component will use a VerticalLayout by default where the data is positioned from top to bottom, but a TiledRowsLayout may be used to position data in a grid instead.

7.4.2. How to download Feathers?

Similar to Starling, there are two ways to download Feathers.

  1. Download the latest stable release as a ZIP file from feathersui.com.

  2. Clone the Feathers Git repository.

As with Starling, the advantage of downloading a stable release of Feathers is that you get a precompiled SWC that is easily linked to your project.

If you clone the Feathers repository, you’ll get the latest bug fixes and new features that are in development (with that in mind, things may be a little unstable in the repository sometimes). To download the Feathers repository, run the following command:

git clone https://github.com/BowlerHatLLC/feathers.git

You will find the source code for Feathers in the source directory, and you may add that directory to your project’s source path the same way you did it with Starling source code.

7.4.3. Resources

Feathers Forum

The Starling forum contains a section dedicated specifically to Feathers. Josh Tynjala, the creator of Feathers, and other experts visit frequently to help troubleshoot issues and answer questions.
http://forum.starling-framework.org/forum/feathers

Feathers Help Files

Just like Starling, Feathers provides an online manual that explains how to use the UI components. The online manual is updated with each new version of Feathers, but you can also view the help files offline when you download a specific Feathers release.
https://feathersui.com/help/

Feathers API Reference

For detailed information about every class, method, or property available in Feathers, consult the Feathers API reference.
https://feathersui.com/api-reference/

Social Media

Follow Feathers on Twitter and Facebook to stay up to date with the latest news and information.
https://twitter.com/feathersui
https://facebook.com/feathersui

7.4.4. Example: Volume Slider

Let’s try out the feathers.controls.Slider component to change the volume of some audio. We’ll start by creating the slider, and we set its direction so that the thumb may be dragged left and right:

var slider:Slider = new Slider();
slider.direction = Direction.HORIZONTAL;
addChild(slider);
We could also set the direction property to Direction.VERTICAL to change the slider’s orientation so that the thumb may be dragged up and down instead.

The first thing we need to do is set the range of values. We might set the range from 0 to 100:

slider.minimum = 0;
slider.maximum = 100;

Let’s set our default volume to 80% with the value property:

slider.value = 80;

Next, it’s important to know when the value changes as the player drags the slider’s thumb. Let’s listen for Event.CHANGE:

slider.addEventListener(Event.CHANGE, onSliderChange);

In our listener, we can access the value property of the slider to change the volume of a flash.media.SoundTransform object:

private function onSliderChange(event:Event):void
{
    var transform:SoundTransform = new SoundTransform();
    transform.volume = slider.value / 100;
    soundChannel.soundTransform = transform;
}

We can easily pass the SoundTransform to a SoundChannel that is already playing.

Skinning the slider

A slider consists of two sub-components, the thumb and the track. A user drags the thumb between the two end points of the track.

Slider

In Feathers, both the thumb and track are of type feathers.controls.Button. To customize the appearance of the slider, we need to skin these buttons:

var slider:Slider = new Slider();
slider.thumbFactory = function():Button
{
    var thumb:Button = new Button();
    thumb.defaultSkin = new ImageSkin(thumbTexture);
    return thumb;
};
slider.minimumTrackFactory = function():Button
{
    var track:Button = new Button();
    track.defaultSkin = new ImageSkin(trackTexture);
    return track;
};

We use a feathers.skins.ImageSkin, which adds some interesting new features to a starling.display.Image. For instance, we might want to change the texture displayed by the thumb when the button is down, or pressed:

var skin:ImageSkin = new ImageSkin(thumbTexture);
skin.setTextureForState(ButtonState.DOWN, thumbDownTexture);
thumb.defaultSkin = skin;

7.4.5. Example: High Score List

Next, let’s try out the feathers.controls.List component to create a list of high scores. We’ll start by creating the list and setting its dimensions:

var list:List = new List();
list.width = 200;
list.height = 300;
addChild(list);

We can use Starling’s AssetManager to load the high scores over the network. For brevity, we’re going to skip that part and simply populate the list’s data provider with some sample data. First, we’ll need a class to store the data about each high score:

class HighScore
{
    public function HighScore(playerName:String, score:int, avatarURL:String)
    {
        this.playerName = player;
        this.score = score;
        this.avatarURL = avatarURL;
    }

    public var playerName:String;
    public var score:int;
    public var avatarURL:String;
}

Then, we’ll populate an Array with several instances of our HighScore class:

var items:Array =
[
    new HighScore("Player 1", 12000,
        "https://example.com/player/42/avatar.jpg"),

    new HighScore("Player 2", 10500,
        "https://example.com/player/79/avatar.jpg"),

    new HighScore("Player 3", 9500,
        "https://example.com/player/133/avatar.jpg")
];
list.dataProvider = new ListCollection(items);

We’ll pass that Array to a ListCollection, which the list uses to keep track of its data by listening for events that tell it when items are added, removed, or replaced (among other useful things).

Next, we need to tell the list how to display our HighScore objects. A list creates an item renderer for each item in the data provider. An item renderer is a Feathers component that will access the properties of our HighScore class, and it can use that data to display things like text or images.

Item Renderer

Let’s customize the list’s item renderers by telling them how to render the name of each player as text:

list.itemRendererFactory = function():IListItemRenderer
{
    var itemRenderer:DefaultListItemRenderer = new DefaultListItemRenderer();
    itemRenderer.labelField = "playerName";
    return itemRenderer;
};

You should recall that the HighScore class contains a property named playerName. To render this property as text, we can pass its name as a String to the item renderer’s labelField property. If the labelField is not found, for some reason, the item renderer will simply call toString() on the item.

We’re using the DefaultListItemRenderer component in this example because it’s moderately flexible, in terms of layout and other basic customization options. It’s also possible to create custom item renderer components, if this default item renderer doesn’t meet your needs, but that’s outside the scope of this introduction to Feathers.

Next, let’s render the player’s avatar image in the item renderer:

itemRenderer.iconSourceField = "avatarURL";

In addition to the primary label, an item renderer may also have an icon. Typically, the icon is used to display an image. For convenience, we only need to pass in the image’s URL, and the item renderer will take care of loading the bitmap and converting it to a texture. In the code above, we’ve set the item renderer’s iconSourceField to the avatarURL property of the HighScore class.

Finally, we’ll display the score on the right side of the item renderer:

itemRenderer.accessoryLabelField = "score";

An item renderer may have an accessory, which may display additional text, another image, a Feathers component, or any other type of Starling display object. In this case, we want to display the score as text, so we’ll set the accessoryLabelField to the score property of the HighScore class.

Skinning the list

A List component has several visual parts that may be customized. The first is its background skin. This is the background that fills the entire width and height of the List and is displayed behind the list’s items.

list.backgroundSkin = new ImageSkin(listBackgroundTexture);

If the list is given padding, the backgroundSkin may be seen around the edges of the list’s content, so it’s also a good way to create a border.

Most of the content in a List is inside its item renderers. Previously, we customized the way that item renderers interpreted the properties of each item in the data provider. Let’s look at how we might style them too:

list.itemRendererFactory = function():IListItemRenderer
{
    var itemRenderer:DefaultListItemRenderer = new DefaultListItemRenderer();
    itemRenderer.defaultSkin = new ImageSkin(itemRendererUpTexture);
    return itemRenderer;
};

Similar to a Button component, the DefaultListItemRenderer can use a feathers.skins.ImageSkin as its background to change the texture displayed when the item renderer is in different states (such as the down state, when the item renderer is pressed by a touch or the mouse):

var skin:ImageSkin = new ImageSkin(itemRendererUpTexture);
skin.setTextureForState(ButtonState.DOWN, itemRendererDownTexture);
itemRenderer.defaultSkin = skin;

We can also customize an item renderer’s font styles. In our example above, the item renderer displays a primary label with the name of the player and a secondary, accessory label to display the score. We can style both of these labels differently.

Text Renderers

Before we continue, we should understand a Feathers concept called text renderers. A text renderer is similar to starling.text.TextField, but text renderers are a bit more advanced, with access to extra, low-level customization options. Feathers comes with three built-in text renderers.

  • feathers.controls.text.BitmapFontTextRenderer renders text with bitmap fonts. This is the default text renderer.

  • feathers.controls.text.TextBlockTextRenderer renders text using Flash Text Engine, an advanced text layout engine that supports TrueType fonts.

  • feathers.controls.text.TextFieldTextRenderer renders text using the classic flash.text.TextField, another option for TrueType fonts.

While BitmapFontTextRenderer is the default text renderer used by Feathers, we’re going to use TrueType fonts in the following example, so we should tell Feathers to create a different text renderer by default:

FeathersControl.defaultTextRendererFactory = function():ITextRenderer
{
    return new TextBlockTextRenderer();
};

Now, all of our item renderers and other components will use TextBlockTextRenderer instead of BitmapFontTextRenderer.

Similar to starling.text.TextField, Feathers text renderers may use starling.text.TextFormat to customize font styles. In the following example, we pass a TextFormat object to the item renderer to customize the font styles for its primary label:

itemRenderer.fontStyles = new TextFormat("Arial", 16, 0x999999);

Earlier, we also displayed the score using an accessory label. Let’s supply font styles for this sub-component too. We’ll use the same font, but a different size.

itemRenderer.accessoryFontStyles = new TextFormat("Arial", 12, 0x999999);

There are many more style properties that we can customize on lists and item renderers, especially for changing the layout. For instance, we could display the item renderers in a grid with multiple rows by passing a TiledRowsLayout to the List. Inside the item renderer, we could move the icon from the left side to a position above the primary text using the iconPosition property, or we might align everything to the center or right with the horizontalAlign property.

7.4.6. Roundup

You should now have a basic understanding of Feathers and some of the UI components that are available to you. This introduction only scratches the surface of what Feathers offers, though. Explore the Feathers documentation to learn more!

7.5. Starling Builder

This introduction was written by Johann Huang, the creator of Starling Builder.

Starling Builder is an open source user interface editor for Starling. Built on top of Starling and Feathers, it provides a WYSIWYG editor that allows to create any in-game user interface in minutes.

7.5.1. Background

Compared to "vanilla" AIR, Starling has allowed developers to target a much broader range of mobile devices. There is one drawback, though: it doesn’t work well with AnimateCC (previously Flash Professional). In the old days of the classic Flash API, we used to design a user interface inside Flash Professional, export it to an SWF file, and then load it into the game. This workflow was powerful and convenient for both artists and engineers — but it no longer works due to the lack of Stage3D support.

Starling Builder aims to bring back this power. Since it was built directly with Starling and Feathers, it supports all their features, and all output is guaranteed to be absolutely accurate.

Starling Builder consists of three parts:

Engine

The engine is the core part of the project. It is used to convert json files into Starling display objects and vice versa. You will need to add this library to your game.

Editor

The desktop application with which you design your user interface.

Extension

Provides a way to support custom UI components and themes without the need to recompile the editor.

Starling Builder Graph

In a nutshell, this is how Starling Builder works:

  1. In the editor, you create the display objects and save them to layouts in JSON format.

  2. In the game, the engine (runtime) recreates the display objects from the layouts.

Sounds simple, right? Yes, simplicity was the biggest design goal of the library. It’s not trying to reinvent anything Starling has already done. The engine is only about 1000 lines of code, and that’s all you need to add into your project! Thanks to the simplicity of the design, you can learn the basic usage in minutes and master the rest within a week.

7.5.2. Casino Fun

To cover the usage of Starling Builder, we are going to create a simple app together. We need a fun but simple game with lots of images and buttons — so what would be better suited than a slot machine! Have you seen those dazzling animated screens in a casino? With the help of Starling Builder, we will bring them to your desktop and mobile devices in just a few minutes.

Without further ado, let’s get started by laying out some user interfaces.

Open up the editor. It will prompt you to choose a folder as a workspace. The workspace is a place to save your assets, extensions and settings. Since this is a new project, you can simply pick an empty folder.

Now you need to put your assets into the workspace. By default, the editor will read assets from the textures folder in the workspace (you can configure the asset paths by clicking on Workspace  Workspace Settings). If everything goes well, the assets will show up in the asset tab as shown below.

Starling Builder Assets

The editor supports different Starling and Feathers components as well as any custom UI components loaded from extensions. For simplicity, this game will only use the following Starling components: Image, Button, TextField and Sprite. Here is the layout created with the editor.

Starling Builder Layout
Group your UI components into a sprite if you need to dynamically position them.

Here is a small trick for the editor: sometimes you want to add placeholder assets to try out a specific layout, but you don’t want to actually create them inside the game (e.g. the animal cards on the reels). In this case, enter the custom tab on the right panel and check forEditor. When you test it by clicking on File  Test Game, those cards won’t show up.

7.5.3. Loading the Layout

Now we are ready to create the actual game project. As a preparation, clone or download the official starling-builder-engine repository from GitHub. It contains the source of the engine, as well as a simple scaffold project that will serve as the starting point of our game.

Use your favorite ActionScript IDE to create a new project. Copy the folders src, scaffold/src and scaffold/libs from the engine repository to this project. When you run it now, you should see just a simple "Hello World" TextField.

The HUD we created above will be represented by a class extending starling.display.Sprite. The method UIBuilder.create will recreate our layout inside Starling.

public class HUD extends Sprite
{
    public function HUD()
    {
        var data:Object = JSON.parse(new EmbeddedLayouts.hud());
        var sprite:Sprite = Game.uiBuilder.create(data) as Sprite; (1)
        addChild(sprite);
    }
}
1 This call creates the display objects from the layout data.

Now exchange the placeholder TextField with the HUD class we just created. That’s done in the init method of the Game class.

public class Game extends Sprite
{
    // ...

    private function init():void
    {
        addChild(new HUD());
    }
}

All is looking well so far, isn’t it? However, when you start the project now, you will run into the following error:

[Fault] exception, information=ReferenceError: Error #1065: Variable Button is not defined.

What does it mean?

This is actually the most frequently asked question of Starling Builder users. We need to step aside a little bit to see how the compiler works.

There is a technique in modern compilers called dead code elimination (DCE). In order to reduce the size of the executable, the compiler will only link the code that’s being directly used in the source files. In our project, we didn’t use the Button class yet, so it’s not linked into the project. When the UIBuilder class tries to create a button dynamically from the layout, this causes an exception.

In order to fix that, we need to do something to make the compiler know this class is being used. All we have to do is reference the class somewhere; here, I just add a mock button variable to the HUD. After you use the Button class in your code, which you certainly will, you can remove the mock button.

public class HUD extends Sprite
{
    private var _button:Button; (1)

    public function HUD()
    {
        var data:Object = JSON.parse(new EmbeddedLayouts.hud());
        var sprite:Sprite = Game.uiBuilder.create(data) as Sprite;
        addChild(sprite);
    }
}
1 Let the compiler know we are using the Button class.

That fixes it — and voilà! The layout has been loaded.

The next step is to access the UI components we need to manipulate. One way to do that would be using the getChildByName API. The problem, however, is that you will end up writing a lot of boilerplate code, and it will be even more tedious to access nested elements.

So, is there a better way? I’m glad you ask!

When creating the layout in Starling Builder, you can assign names to each object (in the properties tab on the right). Use the same names for the member variables of your class, and Starling Builder will bind them to the correct objects — if you tell it to. That’s done by the binder parameter of the UIBuilder.create method.

public class HUD extends Sprite
{
    public var _root:Sprite;

    public var _topContainer:Sprite;
    public var _slotContainer:Sprite;

    public var _balanceText:TextField;
    public var _addBalanceButton:Button; (1)

    // ...

    public function HUD()
    {
        var data:Object = JSON.parse(new EmbeddedLayouts.hud());
        Game.uiBuilder.create(data, true, this); (2)
        addChild(_root);
    }
}
1 Now you are using an actual button! You can safely remove the previous mock button.
2 This is where all the magic happens. Note how we pass this as third parameter. Everything starting with an underscore will be bound after this call.

Internally, the engine will check recursively for any display objects with names starting with an underscore, and try to assign them to the public variable of the binder class with the same name. So all you need to do is declare the elements you want to access and they will be ready to use immediately after calling UIBuilder.create().

You may feel a little bit uncomfortable with prefixing a public variable with an underscore, since the convention is to only do that for private members. The reason is that it’s impossible to access a private (or protected) variable from outside the class in ActionScript 3. Consider this as a trade-off for convenience — it’s totally worth it.

Now let’s make use of our bound display objects and set them up dynamically:

public class HUD extends Sprite
{
    public var _root:Sprite;

    public var _topContainer:Sprite;
    public var _slotContainer:Sprite;

    public var _balanceText:TextField;
    public var _addBalanceButton:Button;

    // ...

    public function HUD()
    {
        var data:Object = JSON.parse(new EmbeddedLayouts.hud());
        Game.uiBuilder.create(data, true, this);
        addChild(_root);

        var stage:Stage = Starling.current.stage;
        var stageWidth:Number = stage.stageWidth;
        var stageHeight:Number = stage.stageHeight;

        const margin:Number = 40; (1)
        _topContainer.x = stageWidth - _topContainer.width - margin;
        _topContainer.y = margin;

        _slotContainer.x = (stageWidth  - _slotContainer.width)  / 2; (2)
        _slotContainer.y = (stageHeight - _slotContainer.height) / 2;

        _balanceText.text = "999"; (3)
        _addBalanceButton.addEventListener(Event.TRIGGERED, onAddBalance); (4)
    }

    private function onAddBalance(event:Event):void
    {
        trace("adding balance...");
    }
}
1 Anchor the top container to the top left of the screen with a margin of 40 points.
2 Center the slot container on the screen.
3 Assign the balance text.
4 Add an event listener to the button that will be called when it’s pressed.
Starling Builder HUD

7.5.4. Under the Hood

Now that the user interface is in place, let’s focus on the core game mechanic. For this simple demo, we are going to use a three reels slot machine and three different cards. How are we going to calculate the pay table for each match? It’s all about math, or, to be more specific, probability. Let’s review some basic probability theory from high school.

If you toss a 6-face dice, what’s the probability of getting 1? You know from your experience it’s 1/6. Why? Because there are 6 faces in total and there is only one face with 1. So we have

Probability

You may think of a reel like a dice with all the faces piled in a column. Each time it stops, a random position in the column will be selected. What’s different is that we can set up the elements of each column arbitrarily.

Let’s use the layout shown below. The 3 columns represent 3 reels in the slot machine. Each column can be rotated individually.

Slot Machine Reels

For example, the probabilities of ending up with a zero in each column are the following:

Column 1: p(0) = 3/5
Column 2: p(0) = 1/3
Column 3: p(0) = 1/4

To get to the combined probability of hitting three zeros, we multiply the individual probabilities. Repeating this for all numbers, we are going to end up with the following probabilities:

p(all 0) = 3/5 × 1/3 × 1/4 = 1/20
p(all 1) = 1/5 × 1/3 × 2/4 = 1/30
p(all 2) = 1/5 × 1/3 × 1/4 = 1/60

The bank always wins, right? So we need to assign the winnings in a way that the expected payout for one credit is below 1 (typically, it’s between 0.8 and 1). In the sample code, I decided to settle on 5, 10, and 20 credits, respectively.

expected payout = 1/20 × 5 + 1/30 × 10 + 1/60 × 20 = 0.92

This means for 1 credit you bet, you get 0.92 credits back, on average. Fair enough! You can find the implementation in the SlotMachine class.

7.5.5. Animating the Reels

Now it’s time to hook up the rest of the game. Everything is pretty straight forward except, well, the reels.

There are different techniques to animate the reels. In this demo, we are going to create a render texture for each reel and tween the Image.tileGrid.y property to animate it. Here is the key implementation:

private function initSlot():void
{
    var image:Image;
    var slots:Array = _slotManager.slots;
    var positions:Array = _slotManager.positions;
    var assets:AssetManager = Game.assetManager;

    for (var i:int = 0; i < slots.length; ++i)
    {
        var slot:Array = slots[i];
        var rt:RenderTexture = new RenderTexture(
            CARD_SIZE, CARD_SIZE * slot.length); (1)

        for (var j:int = 0; j < slot.length; ++j) (2)
        {
            image = new Image(assets.getTexture(slot[j]));
            image.y = CARD_SIZE * j;
            rt.draw(image);
        }

        var sprite:Sprite = this["_slot" + i];
        sprite.removeChildren(0, -1, true);

        var reel:Image = new Image(rt); (3)
        reel.width = CARD_SIZE;
        reel.height = CARD_SIZE * 3;
        reel.tileGrid = new flash.geom.Rectangle(
            0, (1 - positions[i]) * CARD_SIZE,
            CARD_SIZE, CARD_SIZE * slot.length);

        sprite.addChild(reel);
    }
}
1 Create a render texture for each reel.
2 Draw the cards into the render texture.
3 Create an image from the render texture and add it to the container.
private function animateSpin():void
{
    var count:int = 0;
    var slots:Array = _slotManager.slots;
    var positions:Array = _slotManager.positions;

    _spinning = true;

    for (var i:int = 0; i < 3; ++i)
    {
        var distance:int = (1)
            ((_prevPositions[i] - positions[i]) + slots[i].length)
            * CARD_SIZE + 2 * CARD_SIZE * slots[i].length * (i + 1);

        _prevPositions[i] = positions[i];
        var reel:Image = this["_slot" + i].getChildAt(0) as Image;
        var duration:Number = distance / SPIN_SPEED;

        Starling.juggler.tween(reel.tileGrid, duration, (2)
        {
            y: reel.tileGrid.y + distance,
            onUpdate: function(r:Image):void
            {
                r.tileGrid = r.tileGrid;
            },
            onUpdateArgs: [reel],
            onComplete: function():void
            {
                ++count;
                if (count == 3)
                {
                    _spinning = false;
                    onSpinComplete();
                }
            }
        });
    }
}
1 Calculate the distance based on the previous and current positions.
2 Tween reel.tileGrid.y according to distance and spinning speed.
Starling Builder Game

You can check out the complete source code of this project on GitHub.

7.5.6. Roundup

There are a lot of benefits to using Starling Builder for laying out user interfaces, even though it’s completely optional. The biggest win is that, by using the editor, you don’t need to compile your project every time you want to adjust some small detail. Developers can hand over the work to the artists, who are way better at getting the job done for their skill sets.

Another gain of using Starling Builder is to learn and experiment with the API. For example, if you want to see the difference between TextField.autoSize and TextField.autoScale, normally you need to setup a test project and compile it a couple of times to see all the different results. With the help of Starling Builder, you can just drag a TextField onto the canvas and choose different values to see how it works immediately. It’s like JSFiddle for Starling!

In addition to the core functionality this chapter introduces, Starling Builder can also handle localization, tweening, theming, custom components, and more. For more information please visit the Starling Builder website!

Just like Starling, Starling Builder is completely free and open source. Feedback and pull requests are more than welcome!

7.6. Summary

This was just a quick overview of some of the available tools and libraries you can use with Starling. Of course, that was just scratching the surface — it would be completely impossible to showcase all of them, let alone explain even one of them thoroughly enough to do it justice.

To all developers that took part in the development of these and other extensions, or simply shared their expertise with the rest of us, I want to use this opportunity to say "Thanks"! To truly thrive, a bird needs a strong flock of friends, and the Starling community has always been the most extraordinary flock of all. I am proud to be part of it!

1. My editor said it’s not polite to swear, but (1) I used an acronym and (2) context loss really s*cks.
1. Don’t pin me down to that, though.