Blog

I write code, this blog is a dream.

2012.12.17Flash Haxe Gaming SDK, their tools, our tools

So Adobe released a few weeks ago its Gaming SDK for flash.

The package includes Starling, Away3D and Feathers, three well know quality open source ActionScript3 libraries.

You can easily find some externs for most of them on http://lib.haxe.org, which is great!

However, during the development of my last game (which used Starling and Haxe) I found out that using haxelibs wasn't enough.

A lot of good stuff is happening in the AS3 side, Starling is improving day after day on Github, users create really useful extensions for it.

Time is precious and we don't want to write externs for everything or rewrite everything in haxe do we?

Thanks to the great haxe/flash support, we can easily use all the good stuff in a matter of seconds, we can hack AS3 code and use the result from Haxe too.

In this post I will try to teach beginners how to fish AS3 :)

  • how to compile a bunch of .as files into a .swf using the Flex SDK.
  • how to use those .swf files in haxe (with no extra work)
  • how to patch things when haxe is not happy with the .swf

We'll end up with the entire Adobe Gaming SDK, and more, for Haxe. But the journey is more important than the destination.

NOTE: If you are not interested in the process and just want the SDK, I created a bunch of github repositories to host the results. See the test repository to learn how to play with it https://github.com/labe-me/haxe-gaming-sdk-test

NOTE: I do not intend to write any extern for these libraries (I am too lazy for that), hence I won't submit them to haxelib. Feel free to use my work in any way you want to create nice haxelib packages. As far as I am concerned I really enjoy the haxelib git support :)

Start.

We are going to compile .as files, you'll need to install the Flex SDK.

Let's learn how it works with Starling.

(1) get the library sources:

$ git clone https://github.com/PrimaryFeather/Starling-Framework

(2) We have to find the root of the library. Search .as files, take a look at the package they declare (for instance package starling.core ) and go up until you get the root. For Starling the root is the Starling-Framework/starling/src directory.

(3) Compile all the .as files to produce a .swc file.

We can do this with the compc command line tool (from the Flex SDK).

compc \
-swf-version 17 \
-source-path Starling-Framework/starling/src \
-include-sources Starling-Framework/starling/src \
-output Starling.swc

Easy, isn't it? Two times the same parameter telling the root of the sources we are trying to build, one output.

The -swf-version is recommended but chances are the swc will work without it.

(4) Extract the .swf file which is hidden in the .SWC

$ unzip Starling.swc
$ mv library.swf Starling.swf
$ rm catalog.xml

Ok now let's use this SWF, we'll encounter a useful error for the next lesson...

// File StarlingTest.hx
import starling.text.TextField;
import starling.core.Starling;

class StarlingTest extends starling.display.Sprite { public function new(){ super(); var textField = new TextField(400, 300, "Starling!"); addChild(textField); }

public static function main(){ var s = new Starling(StarlingTest, flash.Lib.current.stage); s.start(); } }

Compile (notice the -swf-lib Starling.swf):

haxe \
-swf-lib Starling.swf \
-swf-version 11 \
-main StarlingTest \
-swf-header 640:480:60:aaaaaa \
-swf test.swf

Error:

Starling.swc@starling.core.RenderSupport:1: character 0 : Same field name can't be use for both static and instance : clear
Starling.swc@starling.core.Starling:1: character 0 : Same field name can't be use for both static and instance : context
Starling.swc@starling.core.Starling:1: character 0 : Same field name can't be use for both static and instance : juggler
Starling.swc@starling.core.Starling:1: character 0 : Same field name can't be use for both static and instance : contentScaleFactor

What Haxe is telling us is that it does not accept a class looking like:

class T {
  static var context : String;
  var context : String:
  public function clear(){}
  public static function clear(){}
}

You cannot have both a static and a non static member or function with the same name.

When you encounter this kind of error, you have to create a patch file like this:

//Starling.patch
-starling.core.RenderSupport.clear
-starling.core.Starling.context
-starling.core.Starling.juggler
-starling.core.Starling.contentScaleFactor

And use like this:

haxe \
-swf-lib Starling.swf \
--macro "patchTypes('Starling.patch')" \
-swf-version 11 \
-main StarlingTest \
-swf-header 640:480:60:aaaaaa \
-swf test.swf

This tell haxe to ignore these static fields. We won't be able to use them from our Haxe code but they are available from the Starling instance anyway.

And voila for Starling.

Now let's add Feathers to our tool-belt, we will learn a new thing along the way.

$ git clone https://github.com/joshtynjala/feathers

The source root is easy, it's the source directory.

Compiling the SWC will give us a lot of errors and warnings.

$ compc \
-swf-version 17 \
-source-path feathers/source \
-include-sources feathers/source \
-output Feathers.swc

Warning: Definition starling.display.DisplayObject could not be found. import starling.display.DisplayObject; ...

Don't panic! Feathers just needs Starling to compile.

The useful compc option is --external-library-path+=Starling.swc

$ compc \
-swf-version 17 \
-source-path feathers/source \
-include-sources feathers/source \
--external-library-path+=Starling.swc \
-output Feathers.swc

Extract the library.swf

$ unzip Feathers.swc
$ mv library.swf Feathers.swf
$ rm catalog.xml

And voila for feathers.

Well nearly.

To use feathers we need a skin. We could create our own… but they already created some nice skins they talk about in their tutorials… is it possible to use them?

Let's try.

$ git clone https://github.com/joshtynjala/feathers-examples

A quick look at the content of this repository shows that it contains a few themes. After reading the Feathers' introduction, I am interested in the MetalWorksMobileTheme. The corresponding folder contains a source sub-folder which contains the good old feathers/themes/MetalWorksMobileTheme.as file. Can you guess what we are going to do? Yes:

$ compc \
-swf-version 17 \
-source-path feathers-examples/MetalWorksMobileTheme/source \
-include-sources feathers-examples/MetalWorksMobileTheme/source \
--external-library-path+=Starling.swc \
--external-library-path+=Feathers.swc \
-output Feathers-MetalWorksMobileTheme.swc

Note that we specified two external library dependencies: Starling.swc and Feathers.swc.

Extract the library.swf (this step won't be required in future version of haxe, haxe svn already supports .swc as -swf-lib parameters):

$ unzip Feathers-MetalWorksMobileTheme.swc
$ mv library.swf Feathers-MetalWorksMobileTheme.swf
$ rm catalog.xml

Let's try feathers now:

// FeathersTest.hx
import starling.core.Starling;
import feathers.themes.MetalWorksMobileTheme;

class FeathersTest extends starling.display.Sprite { public function new(){ super(); var theme = new MetalWorksMobileTheme(this, false); var button = new feathers.controls.Button(); button.label = "Good old button"; button.addEventListener( starling.events.Event.TRIGGERED, function(e){ trace("POP"); } ); addChild(button); }

public static function main(){ var s = new Starling(FeathersTest, flash.Lib.current.stage); s.start(); } }

And the more complicated compile command:

$ haxe \
-swf-lib Starling.swf \
--macro "patchTypes('Starling.patch')" \
-swf-version 11 \
-swf-lib Feathers.swf \
-swf-lib Feathers-MetalWorksMobileTheme.swf \
-main FeathersTest \
-swf-header 640:480:60:aaaaaa \
-swf test.swf

It is a good idea to learn how haxelib works to create packages and simplify those compilation parameters… later.

Let's see, what do we miss to par the Adobe Gaming SDK? Oh yes Away3D of course.

$ git clone https://github.com/away3d/away3d-core-fp11

away3d-core-fp11/src/ seems to be the root of the package, let's rock:

$ compc \
-swf-version 17 \
-source-path away3d-core-fp11/src \
-include-sources away3d-core-fp11/src \
-output Away3d.swc

This one will produce some warnings, you are free to fix them and submit your patch to the Away3D team to improve the project, but don't worry it will work :)

Extract the library.swf

$ unzip Away3d.swc
$ mv library.swf Away3d.swf
$ rm catalog.xml

And test:

// Away3DTest.hx
import flash.display.BitmapData;
import away3d.materials.TextureMaterial;
import away3d.textures.BitmapTexture;

class Away3DTest { static var view : away3d.containers.View3D;

public static function main(){ view = new away3d.containers.View3D(); view.camera.x = 300; view.camera.z = -600; view.camera.y = -600; view.camera.lookAt(new flash.geom.Vector3D(0,0,0)); flash.Lib.current.addChild(view); var cubeBmd = new BitmapData(128, 128, false, 0x0); cubeBmd.perlinNoise(70, 207, 5, 123, true, true, 70, true); var cubeTexture = new BitmapTexture(cubeBmd); var cubeMaterial = new TextureMaterial(cubeTexture); cubeMaterial.gloss = 20; cubeMaterial.ambientColor = 0x808080; cubeMaterial.ambient = 1; var geom = new away3d.primitives.CubeGeometry(300, 300, 300); var cube = new away3d.entities.Mesh(geom, cubeMaterial); cube.x = 0; cube.y = 150; view.scene.addChild(cube); flash.Lib.current.addEventListener( flash.events.Event.ENTER_FRAME, onEnterFrame ); }

static function onEnterFrame(e){ view.render(); } }

And the compilation command line:

$ haxe \
-main Away3DTest \
-swf-lib Away3d.swf \
-swf-header 640:480:60:aaaaaa \
-swf-version 11 \
-swf test.swf

This is it for the Adobe Gaming SDK… but we want more, we want the almighty Flash Haxe Gaming SDK :)

So let's add this great starling extension I'd like to use in my future games:

$ git clone https://github.com/PrimaryFeather/Starling-Extension-Particle-System

Compile:

$ compc \
-swf-version 17 \
-source-path Starling-Extension-Particle-System/src \
-include-sources Starling-Extension-Particle-System/src \
-output Starling-Extension-Particle-System.swc \
--external-library-path+=Starling.swc

Extract the swf (we could easily create a small script to automate all this and more):

$ unzip Starling-Extension-Particle-System.swc
$ mv library.swf Starling-Extension-Particle-System.swf
$ rm catalog.xml

Again you will need a patch to use this swf (static methods conflicting with instance methods, like usual, nothing difficult really, just read haxe output):

//Starling-Extension-Particle-System.patch
-starling.extensions.ColorArgb.fromArgb
-starling.extensions.ColorArgb.fromRgb

Test it (you will need these two files created with ParticleDesigner: pdesign.pex, pdesign.png

//ParticlesTest.hx
import starling.core.Starling;
import starling.textures.Texture;
import starling.extensions.PDParticleSystem;

@:bitmap("pdesign.png") class Particle extends flash.display.BitmapData {} @:file("pdesign.pex") class Config extends flash.utils.ByteArray {}

class ParticlesTest extends starling.display.Sprite { public function new(){ super(); var config = new flash.xml.XML(new Config().toString()); var texture = Texture.fromBitmapData(new Particle(0,0)); var system = new PDParticleSystem(config, texture); system.emitterX = flash.Lib.current.stage.stageWidth / 2; system.emitterY = flash.Lib.current.stage.stageHeight / 2; addChild(system); Starling.juggler.add(system); system.start(); }

public static function main(){ var s = new Starling(ParticlesTest, flash.Lib.current.stage); s.start(); } }

Which we compile with the following command line:

$ haxe \
-swf-lib Starling.swc \
--macro "patchTypes('Starling.patch')" \
-swf-version 11 \
-swf-lib Starling-Extension-Particle-System.swc \
--macro "patchTypes('Starling-Extension-Particle-System.patch')" \
-main ParticlesTest \
-swf-header 640:480:60:000000 \
-swf test.swf

Shabang.

Now let's organize and simplify this mess.

I created some git repositories on github:

Thanks to the new haxelib's git support I can install them with these commands:

haxelib git away3d https://github.com/labe-me/haxe-away3d haxelib
haxelib git starling https://github.com/labe-me/haxe-starling haxelib
haxelib git feathers https://github.com/labe-me/haxe-feathers haxelib
haxelib git starling-particle-system https://github.com/labe-me/haxe-starling-particle-system haxelib

Here's how I created these repositories:

I created the haxe-starling repository on github, then:

# clone
git clone git@github.com:labe-me/haxe-starling.git
cd haxe-starling
# link the real AS3 Starling-Framework repository to my repository
git submodule add https://github.com/PrimaryFeather/Starling-Framework starling
# create a separate haxelib folder
mkdir haxelib

I created a Makefile to automate the Starling.swf creation. It basically does what we did above. (https://github.com/labe-me/haxe-starling/blob/master/Makefile)

I put the Starling.patch file we created inside the haxelib folder. (https://github.com/labe-me/haxe-starling/blob/master/haxelib/Starling.patch)

I created an extraParams.hxml file. When the library will be used via -lib starling, haxe will use the content of this file to add some parameters to the compiler. We tell haxe to include our Starling.swf and to patch it with the provided patch. This way we won't have to bother about it anymore. Stage3D also requires swf-version 11. (https://github.com/labe-me/haxe-starling/blob/master/haxelib/extraParams.hxml).

I also created an include.nmml file inside haxelib. This file is automatically used by NME to do the same as the extraParams.hxml file. (https://github.com/labe-me/haxe-starling/blob/master/haxelib/include.nmml).

I created a small test and told haxelib to use my local version of the library:

$ haxe dev starling `pwd`/haxelib

The process was the same with the other repositories.

During this experiment, I found two bugs which you may encounter if you try (I used haxe svn version):

  • Haxe has problems parsing the swf generated by compc if you add the -debug flag to your compilation. The bug is reported and I am pretty sure Nicolas will want to correct it.
  • I experienced problems with the extraParams.hxml feature, I had to specify -swf-lib MyLibrary.swf by hand when two extraParams.hxml where active, say Starling and Feathers (https://github.com/labe-me/haxe-feathers/blob/master/test/Makefile). I bet this bug will be corrected in no time too :)

That's it.

I hope this really big post will be useful to the Haxe community and will help fill the gap between Haxe and AS3.

Lastly, a few thoughts which may help the community save some energy:

I think that writing externs is unrewarding monkey work. I did it for three.js and a few other libs and I mostly hated it. I think that in the case of openly typed platforms like Flash and maybe Java and C# we shouldn't have to do it. The compiler should be able to -jar-lib or -cs-lib just like it can -swf-lib.

Moreover, taking an existing successful project and porting it to Haxe is only interesting if you intend to make it cross-platform. If you only target the original platform, chances are the original project will improve and be fixed faster than your one man port.

No offense intended of course, these are just personal advises coming from experience and time lost :)

If you find any mistake or have any question, please comment, I will be happy to help!

And Happy end of the world.