A week ago, Emanuele Feronato posted his Toony Flash game prototype, showing off a simple prototype that recreates the core gameplay from the online matching game, Toony.

Emanuele mentioned that he “would love to see it ported to mobile device,” so sure enough someone ported it the next day to run on iOS, using Corona.

Not to be out-done, Julian Liebl from Northlight Games brought the game to Haxe, running it on multiple platforms using NME.

Unlike Corona, NME is free, open-source, and targets iOS, Android, BlackBerry, webOS, Windows, Mac, Linux, Flash and even HTML5 from the same codebase.

I loved seeing Julian’s version, but I couldn’t help but wonder if we could bring the game over to NME using even fewer changes to Emanuele’s original.

Project File

The first thing I needed was a project file. NME uses this to know how to build for each platform.

Here is a simple NMML file I created:

<?xml version = "1.0" encoding = "utf-8" ?> <project > <meta title = "Toony" package = "com.emanueleferonato.toony" version = "1.0.0" /> <app main = "ToonyMain" /> <window background = "#000000" /> <ndll name = "std" /> <ndll name = "regexp" /> <ndll name = "zlib" /> <ndll name = "nme" haxelib = "nme" /> <haxelib name = "nme" /> <library path = "toony.swf" /> </project > <?xml version="1.0" encoding="utf-8"?> <project> <meta title="Toony" package="com.emanueleferonato.toony" version="1.0.0" /> <app main="ToonyMain" /> <window background="#000000" /> <ndll name="std" /> <ndll name="regexp" /> <ndll name="zlib" /> <ndll name="nme" haxelib="nme" /> <haxelib name="nme" /> <library path="toony.swf" /> </project>

Code Changes

With the project file out of the way, how much code would we need to change? Not much!

--- /home/joshua/Desktop/AS3/Main.as +++ /home/joshua/Desktop/Haxe/ToonyMain.hx @@ -1,20 +1,25 @@ -package { +package; import flash.display.Sprite; import flash.events.MouseEvent; import flash.events.Event; import flash.utils.Timer; import flash.events.TimerEvent; - public class Main extends Sprite { + import flash.Lib; + import flash.Vector; + class ToonyMain extends Sprite { // variable to see which toony I am moving, if any. Starts at null value because // I am not moving any toony at the beginning of the game - private var movingToony:Toony=null; + private var movingToony:Toony; // timer to handle new toony creation. A new toony will be created every 2 seconds - private var theTimer:Timer=new Timer ( 2000 ) ; + private var theTimer:Timer; // vector to manage all toonies - private var toonyVector:Vector.<Toony>=new Vector.<Toony> ( ) ; - public function Main ( ) { + private var toonyVector:Vector<Toony>; + public function new ( ) { + super ( ) ; + theTimer = new Timer ( 2000 ) ; + toonyVector = new Vector<Toony> ( ) ; // creation of the four draggable toonies - for ( var i:int= 1 ; i<= 4 ; i++ ) { + for ( i in 1...5 ) { var toony:Toony=new Toony ( ) ; addChild ( toony ) ; toony.gotoAndStop ( i ) ; @@ -27,32 +32,34 @@ // main game loop addEventListener ( Event.ENTER_FRAME,update ) ; // event to be triggered when the player releases the mouse button - stage.addEventListener ( MouseEvent.MOUSE_UP,toonyReleased ) ; + Lib.current.stage.addEventListener ( MouseEvent.MOUSE_UP,toonyReleased ) ; // event to be triggered avery two seconds, to generate a new toony theTimer.addEventListener ( TimerEvent.TIMER,newToony ) ; // starting the timer theTimer.start ( ) ; } // what happens when the player presses the mouse on a toony? - private function toonyClicked ( e:MouseEvent ) :void { + private function toonyClicked ( e:MouseEvent ) :Void { // if I am not moving any toony... if ( movingToony==null ) { // setting the toony I am about to move to the toony I just pressed the mouse on - movingToony=e.target as Toony; + movingToony=cast e.target; } } // what happens when the player releases the mouse? - private function toonyReleased ( e:MouseEvent ) :void { + private function toonyReleased ( e:MouseEvent ) :Void { // if I am moving a toony... if ( movingToony!=null ) { // looping through toonies vector - for ( var i:int=toonyVector.length- 1 ; i>= 0 ; i-- ) { + var i:Int = toonyVector.length- 1 ; + while ( i >= 0 ) { // if I am touching a falling toony with the same shape as the toony I am currently moving... // ( that is: if both toonies are showing the same frame... ) if ( toonyVector [ i ] .hitTestPoint ( mouseX,mouseY,true ) &&movingToony.currentFrame==toonyVector [ i ] .currentFrame ) { // the toonies match!! Highlighting the falling toony toonyVector [ i ] .alpha= 1 ; } + i--; } // putting the moved toony back to its place movingToony.y= 430 ; @@ -63,7 +70,7 @@ } } // how do I create a new toony? - private function newToony ( e:TimerEvent ) :void { + private function newToony ( e:TimerEvent ) :Void { // it's simple: I just create a new Toony instance and place it // randomly in the game field with a random frame shown var toony:Toony = new Toony ( ) ; @@ -76,7 +83,7 @@ toonyVector.push ( toony ) ; } // main function - private function update ( e:Event ) :void { + private function update ( e:Event ) :Void { // if I am moving a toony... if ( movingToony!=null ) { // updating toony position according to mouse position @@ -84,7 +91,8 @@ movingToony.y=mouseY; } // looping through toonies vector - for ( var i:int=toonyVector.length- 1 ; i>= 0 ; i-- ) { + var i:Int = toonyVector.length- 1 ; + while ( i>=0 ) { // moving toonies down toonyVector [ i ] .y++; // removing toonies from the stage if they are too close to the bottom of the stage @@ -93,7 +101,13 @@ // removing toony item from toonies vector toonyVector.splice ( i, 1 ) ; } + i--; } } + + public static function main ( ) { + + Lib.current.addChild ( new ToonyMain ( ) ) ; + + } } - } --- /home/joshua/Desktop/AS3/Main.as +++ /home/joshua/Desktop/Haxe/ToonyMain.hx @@ -1,20 +1,25 @@ -package { +package; import flash.display.Sprite; import flash.events.MouseEvent; import flash.events.Event; import flash.utils.Timer; import flash.events.TimerEvent; - public class Main extends Sprite { + import flash.Lib; + import flash.Vector; + class ToonyMain extends Sprite { // variable to see which toony I am moving, if any. Starts at null value because // I am not moving any toony at the beginning of the game - private var movingToony:Toony=null; + private var movingToony:Toony; // timer to handle new toony creation. A new toony will be created every 2 seconds - private var theTimer:Timer=new Timer(2000); + private var theTimer:Timer; // vector to manage all toonies - private var toonyVector:Vector.<Toony>=new Vector.<Toony>(); - public function Main() { + private var toonyVector:Vector<Toony>; + public function new() { + super (); + theTimer = new Timer(2000); + toonyVector = new Vector<Toony>(); // creation of the four draggable toonies - for (var i:int=1; i<=4; i++) { + for (i in 1...5) { var toony:Toony=new Toony(); addChild(toony); toony.gotoAndStop(i); @@ -27,32 +32,34 @@ // main game loop addEventListener(Event.ENTER_FRAME,update); // event to be triggered when the player releases the mouse button - stage.addEventListener(MouseEvent.MOUSE_UP,toonyReleased); + Lib.current.stage.addEventListener(MouseEvent.MOUSE_UP,toonyReleased); // event to be triggered avery two seconds, to generate a new toony theTimer.addEventListener(TimerEvent.TIMER,newToony); // starting the timer theTimer.start(); } // what happens when the player presses the mouse on a toony? - private function toonyClicked(e:MouseEvent):void { + private function toonyClicked(e:MouseEvent):Void { // if I am not moving any toony... if (movingToony==null) { // setting the toony I am about to move to the toony I just pressed the mouse on - movingToony=e.target as Toony; + movingToony=cast e.target; } } // what happens when the player releases the mouse? - private function toonyReleased(e:MouseEvent):void { + private function toonyReleased(e:MouseEvent):Void { // if I am moving a toony... if (movingToony!=null) { // looping through toonies vector - for (var i:int=toonyVector.length-1; i>=0; i--) { + var i:Int = toonyVector.length-1; + while (i >= 0) { // if I am touching a falling toony with the same shape as the toony I am currently moving... // (that is: if both toonies are showing the same frame...) if (toonyVector[i].hitTestPoint(mouseX,mouseY,true)&&movingToony.currentFrame==toonyVector[i].currentFrame) { // the toonies match!! Highlighting the falling toony toonyVector[i].alpha=1; } + i--; } // putting the moved toony back to its place movingToony.y=430; @@ -63,7 +70,7 @@ } } // how do I create a new toony? - private function newToony(e:TimerEvent):void { + private function newToony(e:TimerEvent):Void { // it's simple: I just create a new Toony instance and place it // randomly in the game field with a random frame shown var toony:Toony = new Toony(); @@ -76,7 +83,7 @@ toonyVector.push(toony); } // main function - private function update(e:Event):void { + private function update(e:Event):Void { // if I am moving a toony... if (movingToony!=null) { // updating toony position according to mouse position @@ -84,7 +91,8 @@ movingToony.y=mouseY; } // looping through toonies vector - for (var i:int=toonyVector.length-1; i>=0; i--) { + var i:Int = toonyVector.length-1; + while (i>=0) { // moving toonies down toonyVector[i].y++; // removing toonies from the stage if they are too close to the bottom of the stage @@ -93,7 +101,13 @@ // removing toony item from toonies vector toonyVector.splice(i,1); } + i--; } } + + public static function main () { + + Lib.current.addChild (new ToonyMain ()); + + } } -}

Details

If you are not familiar with reading diff files, here is what I had to change:

Change package{} to package;

Change public class to class

Use new for the class constructor name

Add a super(); call

Initialize “theTimer” and “toonyVector” in the constructor

Change void references to Void

Fix the “for” loops

Add a “static main” function

This is obviously a simple example, but you can see that moving Actionscript 3 to Haxe is pretty simple. You can find more about the nuances on this cheat sheet.

Apart from doing a few logistics, Emanuele’s sample runs with NME… even the SWF assets. In fact, the reason why I used “ToonyMain” instead of “Main” when naming the class was to avoid a name collision. I’m including Emanuele’s finished SWF in the project. I didn’t even have to open the Flash IDE to recompile a new SWF.

Get Started

If you would like to try out my version, you can download NME from http://www.haxenme.org/download.

Here is my source code:

Bringing Emanuele’s simple prototype over to NME took 5 minutes. A larger game would have taken more time, but moving from Flash to cross-platform is not as difficult as you might think 🙂

If you are wondering how well NME performs, take a look at some benchmark results. In my own experience, NME can perform up to nine times faster than Adobe AIR, and five times faster than Corona.