Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
14. May 2013

I just recently took a look at available Haxe game engines and decided to take a closer look at two of them, HaxeFlixel and Awe6.  I started with Awe6, as frankly it's the most non-traditional of the two options and as a result, the most interesting to me.

 

Awe6 is a very design pattern heavy framework, built around the concepts of Inversion of Control and Dependency Injection, but reality is, just about every single design pattern that isn't the Singleton make an appearance.  As a direct result though, groking it all can be a bit confusing… my current understand of things took a great deal of digging through code and documentation to arrive at.  That is one immediate problem with Awe6…  the number of examples available can at best be called lacking.

 

I actually prefer going through working code than I do documentation, so this part is a rather large disappointment.  Then again, if I document awe6 usage, I suppose I will have more people interested in the material, double edged sword I suppose.  Anyways, so far as existing code to work from, there is the incredibly simple Hello World example, the demo in github and finally this example, probably the best of the bunch.

 

 

Ok, enough about what other people have done, now lets take a look at what I've discovered.  One of the ongoing trends of Haxe development holds true for Awe6.  If you are developing on Windows using FlashDevelop, the process is amazingly smooth.  If you aren't… it isn't.  I've given in and just work under FlashDevelop, at least when creating my projects.  I deploy to Dropbox then can reboot and test in Mac if needed.  The following instructions apply to Windows/Flashdevelop only.  You can get it working on other OSes, but its a pain.

 

Getting started is brutally simple.  If you haven't already, install Haxe and NME.

Open a command prompt and type haxelib install awe6

Then run the command haxelib run awe6 install

 

That's it.  Assuming you have FlashDevelop installed, you can now create a new awe6 Project via the project menu, otherwise refer to their site for details of working from command line.  It will create a fully fleshed out folder structure for you, but truth is, when just starting out this is a bit daunting.  That said, the Hello World example is a bit too trivial, so I set about writing something that is middle ground… a more advanced Hello World, or less advanced example project, depending on your perspective.

 

This awe6 sample simply loads an image and allows you to manipulate it using the left and right arrows.  The code:

 

 

import awe6.Types;
import nme.display.Bitmap;
import nme.display.BitmapData;
import nme.display.Sprite;
import nme.Assets;
import nme.Lib;

/**
 * ...
 * @author Mike
 */

class Main
{
	static function main()
	{
		var l_factory = new Factory( Lib.current, '<?xml version="1.0" encoding="utf-8"?><data></data>' );
	}
	
	public function new()
	{
		// needed for Jeash
	}
}

class MyImage extends Entity  {
	private var _sprite:Sprite;
	
	public function new( p_kernel:IKernel )
	{
		_sprite = new Sprite();
		super(p_kernel, _sprite);
	}
	
	public override function _init(): Void {
		super._init();
		_sprite.addChild(new Bitmap(Assets.getBitmapData("assets/img/mechwarriorFrameScaled.png")));
	}
	
	override private function _updater(?p_deltaTime:Int = 0): Void
	{
		super._updater(p_deltaTime);
		if (_kernel.inputs.keyboard.getIsKeyDown(EKey.RIGHT))
			_sprite.x += 10;
		if (_kernel.inputs.keyboard.getIsKeyDown(EKey.LEFT))
			_sprite.x -= 10;
	}
}

class Factory extends AFactory {
	public override function createScene( type : EScene) : IScene
	{
		var scene = new Scene(this._kernel, type);
		scene.addEntity(new MyImage(this._kernel),true);
		return scene;
	}
}

 

 

 

Below is the Flash target if you want to play around with it (click it first to give keyboard focus, then price left or right arrow):

 

If you created a Awe6 project, you will notice the one above is a great deal simpler, but all the same basics are there.  Some of the terminology may certainly seem alien to you… Factory? Entity?  But don't worry, its not actually that difficult in the end.

 

Let's start with the Factory…  this is basically the "guts" of your game…  guts actually is probably the wrong analogy… brains would be a more accurate description or perhaps backbone.  Essentially you extend the Factory class to implement specific behaviour to your game by overriding a series of methods.  In our simple example above, we override the createScene method, but there are dozens of other methods and members that you can set.  The full documentation is right here.  Essentially the factory configures/defines your game, handles special actions ( such as the back key ), allows you to provide your own implementation of the Sessions, Preloader, Overlay, etc…  

 

So, if Factory is the brains or backbone of your application, Scenes are the meat of it.  This is how you organize your game into manageable pieces.  You could create a Scene for say… the title screen, one for when the game is playing, one for displaying the high scores, one for credits, one for settings, etc.  On top of this, literally, you have the Overlay, which is basically a top level UI or HUD if you like.  Your Factory class can create one, but I believe you can also create one at the Scene level as well.

 

After Scene, we have entities… simply put, these are "things" in your game, visible or not.  This could be… your main player, an enemy or a simple proximity trigger.  Entities can in turn contain other entities.  A Scene can contain numerous entities which themselves can contain many more.  Each entity will receive an update() call per tick for updating itself.  That's the bare basics of the hierarchy of objects and that just scratches the surface… it doesn't touch on other aspects such as Agendas ( state machines ), Session ( game state ), Preloader ( asset loading/caching ), or many other aspects of the framework.  We also don't touch on the kernel yet, which is a very important concept that we will talk about in a moment.

 

Now, lets take a quick look at how our code works, in order of execution.  When our program's main() is called, we simply create an instance of Factory, a class we define that inherits from AFactory.  We pass in the context as well as a simple empty configuration in this case.  As part of the process ( we will look a bit deeper in a second ) our Factory classes createScene method is called.  In createScene, we create a simple scene and add a MyImage entity to it. MyImage is a simple Entity that contains a Sprite class holding our mech image.  In the updater method ( called each tick when the entity needs to update ), we check the kernel for input, and move our position accordingly.

 

Now, understanding exactly what is going on behind the scenes is probably the trickiest part, so lets take a closer look at the process.

 

As we mentioned earlier, the Factory class is critical to your game, forming it's backbone, setting major configuration settings and perhaps most importantly controlling scene creation.  This is where execution begins.  You inherit from AFactory, but awe6 remaps it to an appropriate version depending on your platform ( you can see this logic in action right here, the same trick is performed for a number of classes ), all of the platform specific implementations simply provide platform specific functionality, but ultimately inherit from this guy, AFactory.hx.  It's kinda confusing at first, but you only really need to involve yourself in the details if you are trying to grok the source code.  The most important part of of AFactory to understand at this point is this call:

 

inline private function _init():Void
{
#if haxe3
config = new Map<String,Dynamic>();
#else
config = new Hash<Dynamic>();
#end
_configure( true );
_driverInit();
}

 

The call _driverInit() is of critical importance.  This is where the kernel is created.  What is the kernel, other than the object we keep passing around?  Well if Factory is the backbone of your game, Kernal is the backbone of awe6.  Essentially, kernel IS the game engine, and ultimately it is created by your Factory class ( or more specifically, the class your factory inherits from ).  So, obviously kernel is important, let's take a look at it's Init() method, it will make a great deal of things clear:

 

override private function _init():Void

{

super._init();

_view = new View( this, _context, 0, this );

_processes = new List<IProcess>();

_helperFramerate = new _HelperFramerate( factory.targetFramerate );

_isPreloaded = false;

 

// Perform driver specific initializations.

isDebug = factory.isDebug;

isLocal = _driverGetIsLocal();

_driverInit();

 

// Initialize managers.

assets = _assetManagerProcess = new AAssetManager( _kernel );

audio =_audioManager = new AudioManager( _kernel );

inputs = _inputManager = new InputManager( _kernel );

scenes = _sceneManager = new SceneManager( _kernel );

messenger = _messageManager = new MessageManager( _kernel );

_view.addChild( _sceneManager.view, 1 );

_addProcess( _assetManagerProcess );

_addProcess( _inputManager );

_addProcess( _sceneManager );

_addProcess( _messageManager );

_addProcess( _audioManager );

 

// Set defaults for visual switches.

isEyeCandy = true;

isFullScreen = false;

 

// Signal completion to the factory and initialize factory-dependent components.

factory.onInitComplete( this );

 

session = factory.createSession();

session.reset();

_preloader = factory.createPreloader();

_addProcess( _preloader );

_view.addChild( _preloader.view, 2 );

}

 

This is where the various subsystems are created ( assetManager, audio, input, etc… ) and started running ( via addProcess call ).  Then you will notice the code calls back into the Factory, calling the onInitiComplete method.  At this point, the Factory now has a copy of the kernel and the kernel has a pointer to the factory.  One other very important call for program execution is 

_view.addChild( _sceneManager.view, 1 );

View is something drawn on screen, in this case the main window.  In sceneManager, this is where we go full circle, with the call:

scene = _kernel.factory.createScene( p_type );


This in turn is what calls our derived Factory class's createScene method, causing the Scene to be created, our entity to be added to the scene, etc...

 

The bright side is, you don't really need to know ANY of this to make use of Awe6.  I just personally hate using a framework if I don't understand process flow.  It's a clever architecture, decoupling key systems and allowing for you to organize your own code in a clean manner, while still enabling communication between various systems.


12. May 2013

Now that I have decided to go with Haxe and NME, there is the question of which game engine to use.  You may be thinking to yourself "isn't NME a game engine"?  No, not really, although it performs some game engine-y functions.  NME is more like a cross platform Haxe implementation of a Flash like development environment.  A game engine is built on top of this layer as ideally makes me life easier.  So, what are the options then?

 

Build my own

Well first of all there is the option to use nothing.  NME is fairly high level as it is, so the "cost" of building a game engine on top of it is much lower than with many other language/library combinations.  This has the advantage of removing a layer of code I am not intimately familiar with.  On the other hand, I'm lazy and in the business of creating a game, not an engine.  If someone else wants to do the work for me, and freely at that, who am I to say no?

 

HaxeFlixel

http://www.haxeflixel.com/

Flixel is one of the most common Flash 2D game frameworks, and HaxeFlixel is a Haxe port.  The reference documentation is pretty solid.  I have read that HaxeFlixel is a bit further along than our next entry.  The engine itself is state driven, with a state being the fundamental organization model of your game, while your game loop basically flips between states.  Examples of states would be say… Playing, MainMenu, HighScore, etc…  Flixel targets the most of the major targets including iOS, Android, Mac and Windows as well as Flash.  I don't believe HTML5 is supported.

 Status: Under active development

 

Sample HaxeFlixel code from here. ( a Menu state ) :

package;
 
import org.flixel.plugin.photonstorm.FlxDisplay;
import nme.Assets;
import nme.geom.Rectangle;
import nme.net.SharedObject;
import org.flixel.FlxButton;
import org.flixel.FlxG;
import org.flixel.FlxPath;
import org.flixel.FlxSave;
import org.flixel.FlxSprite;
import org.flixel.FlxState;
import org.flixel.FlxText;
import org.flixel.FlxU;
 
class MenuState extends FlxState
{
    override public function create():Void
    {
        #if !neko
        FlxG.bgColor = 0xff131c1b;
        #else
        FlxG.bgColor = {rgb: 0x131c1b, a: 0xff};
        #end 
        FlxG.mouse.show();
         
        //create a button with the label Start and set an on click function
        var startButton = new FlxButton(0, 0, "Start", onStartClick);
        //add the button to the state draw list
        add(startButton);
        //center align the button on the stage
        FlxDisplay.screenCenter(startButton,true,true);
    }
 
    //The on click handler for the start button
    private function onStartClick( ):Void
    {
        //Tell Flixel to change the active game state to the actual game
        FlxG.switchState( new PlayState( ) );
    }
     
    override public function destroy():Void
    {
        super.destroy();
    }
 
    override public function update():Void
    {
        super.update();
    }
}

 

 

HaxePunk

http://haxepunk.com/

HaxePunk is another popular Flash game framework that was ported to Haxe.  Instead of being organized around States like Flixel, HaxePunk is built around entities and scenes, a rather more traditional design.  Haxepunk seems to support the same targets as HaxeFlixel, which means most of the CPP targets ( iOS, Android, Windows, Mac, etc… ) but no HTML5.  Reference docs are pretty complete.

Status: Under active development.

 

Sample code taken from here.

 

 

package scenes;
 
import com.haxepunk.Scene;
import com.haxepunk.HXP;
 
class GameScene extends Scene
{
    public function new()
    {
        super();
    }
 
    public override function begin()
    {
        add(new entities.Ship(16, HXP.halfHeight));
        spawn(); // create our first enemy
    }
 
    public override function update()
    {
        spawnTimer -= HXP.elapsed;
        if (spawnTimer < 0)
        {
            spawn();
        }
        super.update();
    }
 
    private function spawn()
    {
        var y = Math.random() * HXP.height;
        add(new entities.Enemy(HXP.width, y));
        spawnTimer = 1; // every second
    }
 
    private var spawnTimer:Float;
}

 

 

Firmament Game Engine

http://martamius.github.io/Firmament.hx/

Another Haxe 2D game engine, this one supports almost every platform, and unlike Flixel and FlashPunk, that includes HTML5.  Like the others, it is completely open source.  Reference docs are OK, but a little light on description.

Status: Last github commit was 2 months ago as of writing.

No sample code located.

 

Stencyl

http://www.stencyl.com/

Stencyl is an interesting option.  It's a bit higher level than the other frameworks we mentioned earlier, as you can see directly below, Stencyl has an IDE.

Stencyl
In some ways, its much more similar to higher level tools like Construct 2 or Gamemaker in that you visually author your game.  Stencyl however is powered by Haxe and you can drop down to the Haxe code level if you want.  That said, it is a commercial product, so if you want to publish to iOS or ( I believe… the site doesn't make it obvious either way ) Android, you need to pay.  Documentation is pretty solid.
 
Status: Commercial product under active development.
 
No sample in this case due to the unique nature of the product.
 
Stencyl is certainly worth checking out, but probably not a good fit for the project I am working on, as I want the blog posts to be code focused and Stencyl abstracts most of that away. 

 

Citrux Engine

https://github.com/alamboley/CitruxEngine

CitruxEngine is a port of the Flash based game engine.  Sadly it seems to have been abandoned 

Status: Last update was 8 months ago.  May simply be complete but on first glance appears to be a dead end.

Sample code from here.

 

 

package fr.aymericlamboley.test;

import aze.display.SparrowTilesheet;
import aze.display.TileLayer;

import box2D.dynamics.contacts.B2Contact;

import com.citruxengine.core.CitruxEngine;
import com.citruxengine.core.State;
import com.citruxengine.math.MathVector;
import com.citruxengine.objects.CitruxSprite;
import com.citruxengine.objects.Box2DPhysicsObject;
import com.citruxengine.objects.platformer.box2d.Baddy;
import com.citruxengine.objects.platformer.box2d.Coin;
import com.citruxengine.objects.platformer.box2d.Crate;
import com.citruxengine.objects.platformer.box2d.Hero;
import com.citruxengine.objects.platformer.box2d.MovingPlatform;
import com.citruxengine.objects.platformer.box2d.Platform;
import com.citruxengine.objects.platformer.box2d.Sensor;
import com.citruxengine.physics.Box2D;
import com.citruxengine.utils.ObjectMaker;
import com.citruxengine.view.spriteview.SparrowAnimationSequence;
import com.citruxengine.view.spriteview.SpriteLoqAnimationSequence;
import com.citruxengine.view.spriteview.SpriteView;

import com.eclecticdesignstudio.spritesheet.SpriteSheet;
import com.eclecticdesignstudio.spritesheet.importers.SpriteLoq;

import format.SWF;

import nme.Assets;
import nme.geom.Rectangle;

class GameState extends State<GameData> {

    public function new() {

        super();
    }

    override public function initialize():Void {

        super.initialize();

        _ce.gameData.dataChanged.add(_gameDataChanged);

        var box2d:Box2D = new Box2D("Box2D");
        //box2d.visible = true;
        add(box2d);

        //ObjectMaker.FromMovieClip(new SWF(Assets.getBytes("Assets/LevelA1.swf")).createMovieClip());

        var background:CitruxSprite = new CitruxSprite("background", {x:0, y:0, view:"Assets/background.jpg"});
        add(background);

        var physicsObject:Crate = new Crate("physicsObject", 
{x:250, y:200, width:70, height:75, view:"Assets/crate.png"});
        //var physicsObject:PhysicsObject = new PhysicsObject("physicsObject", {x:100, y:20});
        //var physicsObject:PhysicsObject = new PhysicsObject("physicsObject", {x:100, y:20, radius:20});
        add(physicsObject);

        add(new Platform("platform1", {x:498, y:403, width:948, height:20}));
        add(new Platform("platform2", {x:0, y:202, width:20, height:404}));
        add(new Platform("platform3", {x:1278, y:363, width:624, height:20}));
        add(new Platform("platform4", {x:1566, y:165, width:20, height:404}));

        var spriteSheet:SpriteSheet = SpriteLoq.parse(ApplicationMain.getAsset("Assets/heroSpriteLoq.xml"), "Assets");
       
        var tileSheet:SparrowTilesheet = new SparrowTilesheet(Assets.getBitmapData("Assets/heroSparrow.png"), 
Assets.getText("Assets/heroSparrow.xml"));
        var heroTileLayer:TileLayer = cast(view, SpriteView).createTileLayer(tileSheet, "hero");
        var hero:Hero = new Hero("hero", {x:100, y:20, width:60, height:135, 
view:new SparrowAnimationSequence(heroTileLayer, "idle")});

        add(hero);

        spriteSheet = SpriteLoq.parse(ApplicationMain.getAsset("Assets/baddySpriteLoq.xml"), "Assets");
        tileSheet = new SparrowTilesheet(Assets.getBitmapData("Assets/baddySparrow.png"), 
Assets.getText("Assets/baddySparrow.xml"));
        var baddyTileLayer:TileLayer = new TileLayer(tileSheet);
        var baddy1:Baddy = new Baddy("baddy1", {x:440, y:200, width:46, height:68, 
view:new SparrowAnimationSequence(baddyTileLayer, "walk")});
        add(baddy1);

        var coin:Coin = new Coin("Coin", {x:Std.random(400), y:Std.random(300) + 100, radius:30, 
view:"Assets/jewel.png"});
        add(coin);
        coin.onBeginContact.add(_recoltCoin);

        view.setupCamera(hero, new MathVector(320, 240), new Rectangle(0, 0, 1550, 0), new MathVector(.25, .05));
    }

    override public function update(timeDelta:Float):Void {

        super.update(timeDelta);
    }

    private function _gameDataChanged(object:String, value:Dynamic):Void {

        trace(object + " - " + value);
    }

    private function _recoltCoin(ctc:B2Contact):Void {

        var hero:Hero = Std.is(ctc.m_fixtureA.getBody().getUserData(), Hero) ? 
ctc.m_fixtureA.getBody().getUserData() : Std.is(ctc.m_fixtureB.getBody().getUserData(), 
Hero) ? ctc.m_fixtureB.getBody().getUserData() : null;

        if (hero != null) {

            remove(Std.is(ctc.m_fixtureA.getBody().getUserData(), Coin) ? 
ctc.m_fixtureA.getBody().getUserData() : ctc.m_fixtureB.getBody().getUserData());
            _ce.sound.playSound("collect");
        }
    }
}

 

 

Cocos2D for Haxe

https://github.com/ralcr/cocos2d-haxe

It's Cocos2D… for Haxe ported from Cocos2D for iPhone.  I really don't want to try to explain the Cocos2D family tree… it's… confusing.

Status: Last updated on Github 4 months ago.  Not encouraging.

Sample code from here.

 

 

import cocos.support.UIImage;

class Sample_UIImage {

    public function new(){
        var uiimage = new UIImage().initWithContentsOfFile("grossini.png");
        uiimage.onComplete = callback (onComplete, uiimage);
        //flash.Lib.current.addChild ( new flash.display.Bitmap ( new Girl(0,0)));
    }
    function onComplete(uiimage:UIImage) {
        flash.Lib.current.addChild ( uiimage.bitmap );
    }

    public static function main(){
        haxe.Firebug.redirectTraces();
        flash.Lib.current.stage.scaleMode = flash.display.StageScaleMode.NO_SCALE;
        flash.Lib.current.stage.align = flash.display.StageAlign.TOP_LEFT;
        new Sample_UIImage();
    }
}

 

 

Spur

https://github.com/PixelPounce/Spur

A component based game engine.  It appears to be young and hasn't been updated in a long time, a bad combination so I am ignoring it.

 

Hydras

A port of the PushButton engine.  A component based game engine built over top of NME.  Supports many targets, but hasn't been updated in a while.  Documentation?  None, zilch, nodda.  

Status: Last updated about a year ago

Sample code from here:

 

package ;

import com.pblabs.components.scene2D.CircleShape;
import com.pblabs.components.scene2D.SceneAlignment;
import com.pblabs.components.spatial.SpatialComponent;
import com.pblabs.components.tasks.FunctionTask;
import com.pblabs.components.tasks.LocationTask;
import com.pblabs.components.tasks.RepeatingTask;
import com.pblabs.components.tasks.SerialTask;
import com.pblabs.engine.core.PBContext;
import com.pblabs.engine.core.PBGame;
import com.pblabs.engine.core.SignalBondManager;
using Lambda;

using com.pblabs.components.scene2D.SceneUtil;
using com.pblabs.components.tasks.TaskUtil;
using com.pblabs.engine.core.PBGameUtil;
using com.pblabs.engine.util.PBUtil;

class Demo
{
    public function new()
    {
        //Setup logging.
        com.pblabs.engine.debug.Log.setup();

        var game = new PBGame();
        game.addBaseManagers();

        //The main "context". This is equivalent to a level, or a menu screen.
        var context :PBContext = game.pushContext(PBContext);
        //This method is via 'using' SceneUtil
        var scene2D = context.createBaseScene();
        scene2D.sceneAlignment = SceneAlignment.TOP_LEFT;
        var layer = scene2D.addLayer("defaultLayer");

        //Create our blob that we will move around.
        var so = context.createBaseSceneEntity();
        // var blob = context.allocate(com.pblabs.components.scene2D.RectangleShape);
        // blob.borderRadius = 10;
        var blob  = context.allocate(com.pblabs.components.scene2D.CircleShape);
        // blob.radius = 30;

        blob.fillColor = 0xff0000;

        blob.width = 100;
        // blob.height = 300;
        blob.parentProperty = layer.entityProp();
        so.addComponent(blob);
        so.initialize("SomeSceneObj");

        var topLeft = scene2D.getAlignedPoint(SceneAlignment.TOP_LEFT);
        var topRight = scene2D.getAlignedPoint(SceneAlignment.TOP_RIGHT);
        var bottomRight = scene2D.getAlignedPoint(SceneAlignment.BOTTOM_RIGHT);
        var bottomLeft = scene2D.getAlignedPoint(SceneAlignment.BOTTOM_LEFT);

        //This method is via 'using' SceneUtil
        so.setLocation(50, 100);
        //This method is via 'using' TaskUtil
        so.addTask(new RepeatingTask(
            new SerialTask(
                LocationTask.CreateEaseOut(topLeft.x + blob.width / 2, topLeft.y + blob.height / 2, 2),
                LocationTask.CreateEaseOut(topRight.x - blob.width / 2, topRight.y + blob.height / 2, 2),
                LocationTask.CreateEaseOut(bottomRight.x - blob.width / 2, bottomRight.y - blob.height / 2, 2),
                LocationTask.CreateEaseOut(bottomLeft.x + blob.width / 2, bottomLeft.y - blob.height / 2, 2)
            )
            ));

        //Prevents the first frame have the location at (0,0)
        scene2D.update();
    }

    public static function main()
    {
        new Demo();
    }
}

 

Flambe

https://github.com/aduros/flambe

Unfortunately it's web and Flash only so a no-go for me.

 

AWE6

https://code.google.com/p/awe6/

Awe6 is an inversion-of-control / component based game engine.  If you've never heard of IoC or Dependency injection, let me show you this wonderful example from Stack Overflow that shows IoC at it's simplest.

Traditional way:

Class car {

Engine _engine; Public Car() { _engine = new V6(); }

}

Inverted Way

Class car {

Engine _engine;

Public Car(Engine engine) { _engine = engine; }

}

var car = new Car(new V4());

Essentially you are "injecting" functionality into your class, as here, you "Inject" the engine type into the car via the constructor.

Alright, enough about IoC and Dependency Injection, back to Awe6.

Awe6Overview

The above graphic is the overview from the Awe6 site.  The framework has fairly good documentation.

Status: Most recent change was a couple days ago:

Sample code from here:

 

 

package demo.scenes;
import awe6.core.Scene;
import awe6.extras.gui.Text;
import awe6.interfaces.EAudioChannel;
import awe6.interfaces.EMessage;
import awe6.interfaces.EScene;
import awe6.interfaces.ETextStyle;
import awe6.interfaces.IEntity;
import awe6.interfaces.IKernel;
import demo.AssetManager;
import demo.entities.Bouncer;
import demo.entities.Sphere;
import demo.Session;

class Game extends AScene
{
    public static inline var TIME_LIMIT = 30;
    private var _timer:Text;
    private var _score:Int;

    override private function _init():Void
    {
        super._init();
        isPauseable = true;
        isSessionSavedOnNext = true;
        _session.isWin = false;
        var l_textStyle = _kernel.factory.createTextStyle( ETextStyle.SUBHEAD );
        #if js
        // for js performance boost (realtime filters very constly)
        l_textStyle.filters = [];
        l_textStyle.color = 0x020382;
        #end
        _timer = new Text( _kernel, _kernel.factory.width, 50, Std.string( 
_tools.convertAgeToFormattedTime( 0 ) ), l_textStyle );
        _timer.y = 70;
        addEntity( _timer, true, 1000 );

        _kernel.audio.stop( "MusicMenu", EAudioChannel.MUSIC );
        _kernel.audio.start( "MusicGame", EAudioChannel.MUSIC, -1, 0, .5, 0, true );
        for ( i in 0...10 )
        {
            addEntity( new Sphere( _kernel ), true, i + 10 );
        }
        _kernel.messenger.addSubscriber( _entity, EMessage.INIT, handleSphere, Sphere );
        _kernel.messenger.addSubscriber( _entity, EMessage.DISPOSE, handleSphere, Sphere );
    }
    
    public function handleSphere( p_message:EMessage, p_sender:IEntity ):Bool
    {
// trace( p_message + " " + p_sender );
        return true;
    }
    

    override private function _updater( ?p_deltaTime:Int = 0 ):Void
    {
        super._updater( p_deltaTime );

        _score = Std.int( _tools.limit( ( 1000 * TIME_LIMIT ) - _age, 0, _tools.BIG_NUMBER ) );
        if ( _score == 0 )
        {
            _gameOver();
        }
        _timer.text = _tools.convertAgeToFormattedTime( _age );
        var l_spheres:Array<Sphere> = getEntitiesByClass( Sphere );
        if ( ( l_spheres == null ) || ( l_spheres.length == 0 ) )
        {
            _gameOver();
        }
    }

    override private function _disposer():Void
    {
        _kernel.audio.stop( "MusicGame", EAudioChannel.MUSIC );
        super._disposer();
    }

    private function _gameOver():Void
    {
        if ( _score > _session.highScore )
        {
            _session.isWin = true;
            _session.highScore = _score;
        }
        _kernel.scenes.next();
    }

}

 

Ash Entity Framework

https://github.com/nadako/Ash-HaXe

This is a Haxe port of the Ash Framework a popular entity framework.  Unlike earlier examples, this is not a game engine, but may be an option as NME provides a great deal of the functionality you would normally require from a game engine.  That said, it is marked as PRE-ALPHA… that's pretty early on.  It's pretty active development wise but Haxe specific documentation is basically non-existent.

Status: Last commit 5 days ago.

Sample code from here:

 

package net.richardlord.asteroids;

import flash.display.DisplayObjectContainer;

import ash.tick.ITickProvider;
import ash.tick.FrameTickProvider;
import ash.core.Engine;

import net.richardlord.asteroids.systems.BulletAgeSystem;
import net.richardlord.asteroids.systems.CollisionSystem;
import net.richardlord.asteroids.systems.GameManager;
import net.richardlord.asteroids.systems.GunControlSystem;
import net.richardlord.asteroids.systems.MotionControlSystem;
import net.richardlord.asteroids.systems.MovementSystem;
import net.richardlord.asteroids.systems.RenderSystem;
import net.richardlord.asteroids.systems.SystemPriorities;
import net.richardlord.asteroids.systems.AnimationSystem;
import net.richardlord.asteroids.systems.DeathThroesSystem;
import net.richardlord.input.KeyPoll;

class Asteroids
{
    private var container:DisplayObjectContainer;
    private var engine:Engine;
    private var tickProvider:ITickProvider;
    private var creator:EntityCreator;
    private var keyPoll:KeyPoll;
    private var config:GameConfig;

    public function new(container:DisplayObjectContainer, width:Float, height:Float)
    {
        this.container = container;
        prepare(width, height);
    }

    private function prepare(width:Float, height:Float):Void
    {
        engine = new Engine();
        creator = new EntityCreator( engine );
        keyPoll = new KeyPoll( container.stage );
        config = new GameConfig();
        config.width = width;
        config.height = height;

        engine.addSystem(new GameManager( creator, config ), SystemPriorities.preUpdate);
        engine.addSystem(new MotionControlSystem( keyPoll ), SystemPriorities.update);
        engine.addSystem(new GunControlSystem( keyPoll, creator ), SystemPriorities.update);
        engine.addSystem(new BulletAgeSystem( creator ), SystemPriorities.update);
        engine.addSystem(new DeathThroesSystem( creator ), SystemPriorities.update);
        engine.addSystem(new MovementSystem( config ), SystemPriorities.move);
        engine.addSystem(new CollisionSystem( creator ), SystemPriorities.resolveCollisions);
        engine.addSystem(new AnimationSystem(), SystemPriorities.animate);
        engine.addSystem(new RenderSystem( container ), SystemPriorities.render);

        creator.createGame();
    }

    public function start():Void
    {
        tickProvider = new FrameTickProvider( container );
        tickProvider.add(engine.update);
        tickProvider.start();
    }
}

 

Please let me know if I have missed any!



Personally I am leading towards Flixel ( community size and maturity level ), but am going to take a closer look at the Awe6 engine first. If neither works for me, I will simply roll me own!


29. April 2013

So we've already look at general development, graphics programming and handling input with Haxe with NME, now we are going to take a look at audio support.  This is by far going to be the shortest post of the series, as frankly, it's the simplest.

 

Let's jump right in with playing audio.

 

 

package gfs;

import nme.Assets;
import nme.display.Sprite;
import nme.Lib;
import nme.media.Sound;

class Main extends Sprite
{
	public function new()
	{
		super();
	    var song = Assets.getSound("audio/background.mp3");
		var soundfx1 = Assets.getSound("audio/effect2.wav");
		
		song.play();
		soundfx1.play();
	}
}

 

 

Well, that was pretty straight forward.  There are only a few things to be aware of here.  First, notice we loaded the sounds using the Assets class with the path "/audio/something".  This path needs to be defined in the NMML file.  Here is what mine looks like:

<assets path="Assets/audio" rename="audio" type="audio" include="*" />

The actual directory on your hard disk should be located within the folder Assets.  The next up thing to know is the file formats.  At the end of the day the supported formats are defined by the SDL_mixer library.  Notice how I used loaded the soundfx from a wav file, while the background music as an MP3?  There is a very important reason for this; you can only have one mp3 playing at a time.  Other more "short term" sound effects should be stored in a  lighter format.  Audio is going to be one of those tricky things, as different platforms can play different formats. It's also important to realize that mp3 is a patent encumbered format, so if you are looking to sell your game, be careful with mp3, or they could come back to you looking for licensing fees!  The ogg vorbis format is a free alternative, but isn't as widely supported.

 

Now let's take a look at a slightly more advanced sample:

 

 

 

package gfs;

import haxe.Timer;
import nme.Assets;
import nme.display.Sprite;
import nme.events.Event;
import nme.Lib;
import nme.media.Sound;
import nme.media.SoundTransform;

class Main extends Sprite
{
	var playLeft = true;
	var song : Sound;
	
	public function new()
	{
		super();
	    song = Assets.getSound("audio/background.mp3");
		var soundfx1 = Assets.getSound("audio/effect2.wav");
		
		var soundChannel = song.play();
		
		// Call our sound effect every second... forever.
		new Timer(1000).run = function() {
			soundfx1.play();
		}
		
		soundChannel.addEventListener(Event.SOUND_COMPLETE, onSongEnd );
	}
	public function onSongEnd(event:Event) {
			Lib.trace("Fired");
			var channel = song.play();
			playLeft = !playLeft;
			
			if (playLeft)
				channel.soundTransform = new SoundTransform(1, -1);
			else
				channel.soundTransform = new SoundTransform(1, 1);

			channel.addEventListener(Event.SOUND_COMPLETE, onSongEnd );
		}
}

 

 

This example is a bit more complex to show off a couple of the features of the audio libraries.  You may notice that play() returns a SoundChannel object.  This object has a number of useful features… you can manipulate the playback of the sound using this guy, by applying SoundTransform's like we do here, to pan sound left or right, you can modify the volume, get current playback position, etc…  In this particular example, we load our two sound effects, start the background music playing, then create a timer that fires ever second, playing our second sound effect over and over and over.

We also wire up an event handler that will be fired when your soundChannel gets to the end ( SOUND_COMPLETE event ).  When that occurs, we toggle the sound to either play only on the left channel, or right channel.  We then recursively wire up our onSongEnd event on our newly created SoundChannel.  This code worked perfectly on every tested platform, although Android had some weird issues… it didn't play properly at first, but once I lost and regained focus, it worked perfectly.

 

So, what about Video?

This is one area current NME is a bit lacking.  There are projects out there that provide native video playback on iOS as well as this project for playing back webm encoded video files, otherwise I believe you are currently out of luck.  So if you need to play cut screens, you are currently left rolling your own solution for each platform.  Fortunately, I do not need video support. :)


27. April 2013

One of the downsides to using the Tilesheet class in NME is that it can't receive events.  Just as earlier on we created a sprite object to hold our bitmap when we wanted it to receive events, how exactly do you handle this when working with the Tilesheet class?  In the Haxe/NME Input tutorial, I got asked exactly this question:

 

I tired to wrap the animation of previous tutorial before into this one.

but since the animation is an tileSheet object it doesn't want to be wrapped into a Sprite
it said

nme.display.Tilesheet should be flash.display.DisplayObject
for:
sprite.addChild(sprites); // sprite is my tileSheet and sprites is a sprite

any idea to solve the problem or it's going to come in a next tutorial

 

Hmmm… good question…  short answer is, I didn't know.  Slightly longer answer is, when you call drawTiles, you can specify where to draw.  In the earlier example we simply drew to the stage.  So, we simply create a Sprite around an empty Bitmap add it to the scene and add the sprite to the scene.

Let's look at some code:

package gfs;

import nme.Assets;
import nme.display.Bitmap;
import nme.display.DisplayObject;
import nme.display.Graphics;
import nme.display.SpreadMethod;
import nme.display.Sprite;
import nme.display.Tilesheet;
import nme.events.Event;
import nme.geom.Rectangle;
import nme.Lib;
import nme.ui.Keyboard;
import nme.events.KeyboardEvent;


class Main extends Sprite
{
	private var currentFrame: Int = 5;
	private  var sprites: Tilesheet;
	var sprite:nme.display.Sprite;
	
	public function new()
	{
		super();
		sprites = new Tilesheet(Assets.getBitmapData("img/mechwarrior.png"));
		var tileData = new Array<Float>();
		
		for (i in 0...11)
		{
			sprites.addTileRect(new Rectangle(0, i * 80, 90, 80));
		}
		
		sprite = new Sprite();
		var bmp = new Bitmap();
		bmp.width = 90;
		bmp.height = 80;

		sprite.addChild(new Bitmap());
		
		sprite.x = 0;
		sprite.y = 0;
		
		sprites.drawTiles(sprite.graphics, [0, 0, currentFrame,4],false,Tilesheet.TILE_SCALE);
		
		Lib.current.stage.focus = sprite;
		sprite.addEventListener(KeyboardEvent.KEY_DOWN, function(e) {
			graphics.clear();
			sprite.graphics.clear();
			if (e.keyCode == Keyboard.DOWN)
			{
				if (++currentFrame == 11) currentFrame = 0;
			}
			else if (e.keyCode == Keyboard.UP)
			{
				if (--currentFrame == 0) currentFrame = 10;
			}
			sprites.drawTiles(sprite.graphics, [0,0, currentFrame,4], false, Tilesheet.TILE_SCALE);
		});
		Lib.current.stage.addChild(sprite);
	}
}

 

You can run this application by clicking here. Press up and down to animate.

 

And there you go.  If you are working with a sprite animation populated by a Tilesheet that you want to receive events directly, this is one option.  A couple of things though…  handling keyboard events in this case works really really really poorly… it only works in Flash…  generally you will want to handle keyboard events at the stage level.  This code was more a proof of concept than anything else.  Simply put, it's probably a really stupid way to do this.


24. April 2013

In the past we covered basic program structure as well as using graphics using Haxe and NME.  In this section we are going to look at handling input.  A number of these samples, such as multi touch, will only work on a device, although we will briefly look at how to create device specific code that still compiles on other platforms.  Alright, let's jump right in.

 

First lets look at the basics of handling touch and mouse click events.

 

Mouse click and finger touch event handling

 package gfs;

import nme.Assets;
import nme.display.Bitmap;
import nme.display.Sprite;
import nme.events.Event;
import nme.events.TouchEvent;
import nme.events.MouseEvent;
import nme.Lib;


class Main extends Sprite
{
	private var sprite:Sprite;
	
	public function new()
	{
		super();
		
		var img = new Bitmap(Assets.getBitmapData("img/mechwarriorFrameScaled.png"));
		
		//Bitmap unfortunately cannot receive events, so we wrap it in a sprite
		sprite = new Sprite();
		sprite.addChild(img);
		
		sprite.x = Lib.current.stage.stageWidth / 2 - sprite.width / 2;
		sprite.y = Lib.current.stage.stageHeight / 2 - sprite.height / 2;
		
		sprite.addEventListener(MouseEvent.CLICK, mechClicked);
		Lib.current.stage.addEventListener(TouchEvent.TOUCH_TAP, onTouch);
		Lib.current.stage.addEventListener(MouseEvent.CLICK, onClick);

		Lib.current.stage.addChild(sprite);
	}
	
	public function mechClicked(event: MouseEvent)
	{
		sprite.x = Lib.current.stage.stageWidth / 2 - sprite.width / 2;
		sprite.y = Lib.current.stage.stageHeight / 2 - sprite.height / 2;
		event.stopImmediatePropagation();
	}
	
	public function onTouch(event: TouchEvent)
	{
		sprite.x = event.stageX - sprite.width / 2;
		sprite.y = event.stageY - sprite.height / 2;
	}
	
	public function onClick(event: MouseEvent)
	{
		sprite.x = event.stageX - sprite.width / 2;
		sprite.y = event.stageY - sprite.height / 2;
	}
	
} 

When you run this code you should see:

1

The mech will move wherever you click or touch.  If you click on the mech sprite, it will move back to the centre of the screen.

 

Now back to the code. I was informed that I didn't need to create a static main function when working with NME, so in my prior examples… ignore that bit. :)

Therefore we now start of in our constructor new.  First we create an image, a small version of our now familiar mech image.  There is a catch though, even though it has the method addEventListener, a Bitmap object can't actually receive events.  Therefore we are creating a Sprite object to hold our Bitmap.  Next up we centre the sprite to the screen.  Then we wire up sprite to receive MouseEvent.CLICK events, which will causer it to call the function mechClicked.  We also wire up TouchEvent.TOUCHT_TAP and MouseEvent.CLICK event handlers to the actual stage.

It's important to realize the order things are handled when a click or touch occurs.  It will ultimately be the object that is touched that get's first crack at the event, then it's parent, then it's parent, etc…  this is called 'bubbling'.  It's a very important thing to get your head around, as consider the current situation…  we are setting up a click handler in our mech sprite that handles the click, causing the sprite to be re-entered back to the middle of the screen if clicked.  Then the stage handles the event and moves the sprite to where the user clicked, effectively overwriting the actions the Sprite's event handler took.  In this case we want to stop handling the event  once the mech is clicked, so it's parent's handler won't run.  This is accomplished by calling event.stopImmediatePropagation().  The only other thing to notice is the different event types passed in to the various event handlers.

There is however one current gotcha in the above code… for CPP targets it doesn't actually work.  The call to stopImmediatePropagation() doesn't actually do what it says.  I reported this on the forums and most impressively, they already fixed the bug!  Colour me impressed on that one.  So, depending on when you are reading this, mechClicked() may not appear to be working if run on one of the CPP targets.  It does however work just fine on Flash and HTML5.

If you aren't working from NME sources, you can work around with this simple fix:

class Main extends Sprite
{
	private var sprite:Sprite;
	var clickHandled = false;
	
	public function new()
	{
		super();
		
		var img = new Bitmap(Assets.getBitmapData("img/mechwarriorFrameScaled.png"));
		
		//Bitmap unfortunately cannot receive events, so we wrap it in a sprite
		sprite = new Sprite();
		sprite.addChild(img);
		sprite.x = Lib.current.stage.stageWidth / 2 - sprite.width / 2;
		sprite.y = Lib.current.stage.stageHeight / 2 - sprite.height / 2;
		
		sprite.addEventListener(MouseEvent.CLICK, mechClicked);
		
		Lib.current.stage.addEventListener(TouchEvent.TOUCH_TAP, onTouch);
		Lib.current.stage.addEventListener(MouseEvent.CLICK, onClick);
		
		
		Lib.current.stage.addChild(sprite);
	}
	
	public function mechClicked(event: MouseEvent)
	{
			sprite.x = Lib.current.stage.stageWidth / 2 - sprite.width / 2;
			sprite.y = Lib.current.stage.stageHeight / 2 - sprite.height / 2;
			clickHandled = true;
	}
	public function onTouch(event: TouchEvent)
	{
		sprite.x = event.stageX - sprite.width / 2;
		sprite.y = event.stageY - sprite.height / 2;
	}
	
	public function onClick(event: MouseEvent)
	{
		if (clickHandled) {
			clickHandled = false;
			return;
		}
		sprite.x = event.stageX - sprite.width / 2;
		sprite.y = event.stageY - sprite.height / 2;
	}
}

Basically you just set a flag that the current click action has been handled.  Of course, this system falls on it's face once you start looking at multi-touch, as we will shortly

 

Keyboard Handling

So that's the basics of clicking and touching, now let's take a look at using keyboard control.  This will only work on devices with a physical keyboard.

package gfs;

import nme.Assets;
import nme.display.Bitmap;
import nme.display.Sprite;
import nme.events.Event;
import nme.events.KeyboardEvent;
import nme.Lib;
import nme.ui.Keyboard;


class Main extends Sprite
{
	private var sprite:Sprite;
	
	public function new()
	{
		super();
		
		var img = new Bitmap(Assets.getBitmapData("img/mechwarriorFrameScaled.png"));
		
		//Bitmap unfortunately cannot receive events, so we wrap it in a sprite
		sprite = new Sprite();
		sprite.addChild(img);
		
		sprite.x = Lib.current.stage.stageWidth / 2 - sprite.width / 2;
		sprite.y = Lib.current.stage.stageHeight / 2 - sprite.height / 2;

		Lib.current.stage.addEventListener(KeyboardEvent.KEY_UP, function(event) {
			if (event.keyCode == Keyboard.RIGHT)
			{
				if (event.altKey || event.ctrlKey || event.shiftKey)
					sprite.x += 10;
				else
					sprite.x += 1;
			}
			if (event.keyCode == Keyboard.LEFT)
			{
				if (event.altKey || event.ctrlKey || event.shiftKey)
					sprite.x -= 10;
				else
					sprite.x -= 1;
			}
		});

		Lib.current.stage.addChild(sprite);
	}
	
	public static function main()
	{
		Lib.current.addChild(new Main());
	}
} 

When you run this code, you can use the left and right arrows to move the sprite left or right.  Holding down the alt key, shift key or control key will cause the sprite to move by 10 pixels, otherwise it moves by 1 pixel in the given direction.

As you can see, handling keyboard events is almost exactly the same as handling mouse and touch events.  You simply wire up the stage to listen for the event KeyboardEvent, in this case KEY_UP.  You simple compare the keyCode of the event against the enum specified in Keyboard to see if your key has been pressed.  There are also a series of flags, altKey, ctrlKey and shiftKey that tell you the status of each of these keys.

One important thing to note, you do not use KeyboardEvent to handle text field input, instead use the methods of TextField.

 

Accelerometer/motion control handling 

Now let's take a look at how to handle the Accelerometer.  This code will compile on any platform, but will only work on mobile platforms for obvious reasons.

 

package gfs;

import nme.Assets;
import nme.display.Bitmap;
import nme.display.Sprite;
import nme.events.Event;
import nme.events.AccelerometerEvent;
import nme.Lib;
import nme.sensors.Accelerometer;
import nme.ui.Accelerometer;


class Main extends Sprite
{
	private var sprite:Sprite;
	
	public function new()
	{
		super();
		
		var img = new Bitmap(Assets.getBitmapData("img/mechwarriorFrameScaled.png"));
		
		//Bitmap unfortunately cannot receive events, so we wrap it in a sprite
		sprite = new Sprite();
		sprite.addChild(img);
		
		sprite.x = Lib.current.stage.stageWidth / 2 - sprite.width / 2;
		sprite.y = Lib.current.stage.stageHeight / 2 - sprite.height / 2;

		Lib.current.stage.addEventListener(Event.ENTER_FRAME, onFrameEnter);
		Lib.current.stage.addChild(sprite);
	}
	
	public function onFrameEnter(event:Event) {
		#if (android || ios)
		if (Accelerometer.isSupported) {
			var y = nme.ui.Accelerometer.get().y;
			Lib.trace(y);
			if (y < 1 && y > 0.6) sprite.y-=5; // straight up and down
			else if (y < 0.4 && y > 0.0) sprite.y+=5;  // turned 90 degrees from user
		}
		else
			Lib.trace("No Accelerometer support");
		#else
			Lib.trace("Not a mobile target");
		#end
	}
	
	public static function main()
	{
		Lib.current.addChild(new Main());
	}
}

When you run this code, our trusty mech sprite is drawn centred to the screen.  If the phone is straight up and down ( parallel to your head ) nothing will happen.  As you tilt the phone away from you, for the first 40 or so degrees the sprite will move up.  Then a dead zone, then for the next 40 os so degrees the sprite will move down, until you are holding the phone perpendicular to you, at which point the sprite will stop moving.

Now for a bit of a red herring… there are Accelerometer events… THEY DONT WORK!  Don't go that route.  The events are simply never fired.  Instead you need to poll the Acelerometer when you want data.  In this case we are going to use the ENTER_FRAME event, which is fired at the beginning of each frame, causing the onFrameEnter function being called.

In onFrameEnter, we have our first compiler conditional.  If you are used to pre-processor directives, this will be familiar with you.  Basically code starting with the # symbol tell the compiler what to id.  In this case, code within the #if (android || ios) will only be executed if those values are true.  If it is an iOS or Android device, we simply read the G value of the y axis.  A value of 1 indicates a motionless phone straight in from of you.  A value of 0 is a motionless phone perpendicular to you.  You can get the motion values of all 3 axis and use them to determine orientation as well as movement.

 

Handling multitouch

Finally let's take a look at multitouch support.  Once again you obviously need a multi-touch capable device to run this sample.

 

 

package gfs;


import nme.Assets;
import nme.display.Bitmap;
import nme.display.Sprite;
import nme.display.Stage;
import nme.display.StageScaleMode;
import nme.events.Event;
import nme.events.TouchEvent;
import nme.Lib;
import nme.ui.Multitouch;
import nme.ui.MultitouchInputMode;


class Main extends Sprite
{
	private var sprites: Array<Bitmap>;
	
	public function new()
	{
		super();
		
		sprites = new Array<Bitmap>();
		
		sprites.push(new Bitmap(Assets.getBitmapData("img/mechwarriorFrame.png")));
		sprites.push(new Bitmap(Assets.getBitmapData("img/mechwarriorFrame.png")));
		sprites.push(new Bitmap(Assets.getBitmapData("img/mechwarriorFrame.png")));
		sprites.push(new Bitmap(Assets.getBitmapData("img/mechwarriorFrame.png")));
		
		for (i in 0 ... sprites.length) {
			sprites[i].x = Std.random(800);
			sprites[i].y = Std.random(1280);
			Lib.current.stage.addChild(sprites[i]);
		}

		Lib.current.stage.addEventListener(TouchEvent.TOUCH_BEGIN, function(e) {
			sprites[e.touchPointID].x = e.localX;
			sprites[e.touchPointID].y = e.localY;
		});
	}

}

Here is the code running on my Galaxy Note.  Each mech sprite is positioned where you touched.  Up to four sprites/touches can be tracked in this example.

Export 04

So, each sprite represents the location I most recently touched with each finger. 

 

We simple create an array of images all holding the same image, one for each of our four fingers.  We then randomize it's starting location ( for no particular reason ) then add it to the stage.  Touch is handled exactly as it was before, the only real difference is the event can be fired a number of times.  You determine if it is multiple touch by checking the touchPointID value of event.  This ID value can be thought of as the index value of your finger… so if you are using a single finger, it will have a value of 0.  If you are using 3 fingers, and it is the third touchpoint, the value will be 2.  

 

One word of warning, there is mention in the docs of GESTURE modes… such as ZOOM, etc… these do not exist in NME.  If you want gesture support, you need to roll it yourself.

 

So that's IO in Haxe…  I have to say, other then a bug ( which was fixed almost instantly! ) and a red herring in the documentation about gesture support, that was a painless experience and has all the functionality I need.  Of course, I just used a portion of what's there… you can also mouse over, key up, mouse enter, etc...

 

I did run into a few annoyances with FlashDevelop though.  First, if you run an cpp target project, while one is already running on your device, not only will it fail, it will bork the debugger forcing you to reset FlashDevelop.  It sounds like a mild annoyance, but I did it probably 15 times!  Another annoyance is Flashdevelop loses intellisense when inside anonymous methods, or whatever Haxe/Flash calls them.


AppGameKit Studio

See More Tutorials on DevGa.me!

Month List