Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
22. April 2013

In the previous post we looked at the basic structure of a program in Haxe/NME running on a number of different platforms.  Now I am going to check out the graphics portion of NME, specifically sprite/bitmap rendering.  I am going to need a graphic to use for this example and I chose to use these, specifically this one.  It's a sprite sheet from Mech Commander 2, a game Microsoft released for free, including all of the source code and assets.  Of course the images themselves are still copy protected, so do not use them in a commercial project!  Of course, you can use whatever image you want, just be aware that i've hard coded the dimensions in the following examples to match the dimensions of these images.

 

I've taken a single frame of animation and blown it up for the first sample.

MechwarriorFrameScaled

We are now going to look at the code required to display this guy on screen.  Since I am using FlashDevelop, I am following their folder structure.  So I've added my image to the folder assets/img as MechWarriorFrameScaled.png.  Your layout should look something like this:

Burp

 

Now let's take a look at the code:

 

 

package gfs;

import nme.Assets;
import nme.display.Bitmap;
import nme.display.Sprite;
import nme.events.Event;
import nme.Lib;
import nme.text.TextField;
import nme.text.TextFormat;

class Main extends Sprite
{
	public function new()
	{
		super();
		var mech:Bitmap = new Bitmap(Assets.getBitmapData("img/mechwarriorFrameScaled.png"));
		Lib.current.stage.addChild(mech);
	}
	
	public static function main()
	{
		// static entry point
		Lib.current.stage.align = nme.display.StageAlign.TOP_LEFT;
		Lib.current.stage.scaleMode = nme.display.StageScaleMode.NO_SCALE;
		Lib.current.addChild(new Main());
	}
}

 Pretty straight forward, in our constructor new() we create a new nme.Display.Bitmap by using the nme.Assets class's getBitmapData function, passing in our file name and directly ( below assets in the hierarchy, you don't need to specify the assets folder in the path ).  We then simply add the bitmap to the stage, which you can get with the static value Lib.current.stage.  We run this code and:

GFX1

 

Well, that was easy enough.  That code works just fine on every single platform I tested, which is most of them.

 

Often what you want to group a number of similar sprites, such as animation frames, together in a single sprite sheet, or in NME parlance, a tile sheet.  Let's take a look at drawing a single frame from a sprite sheet.  The following is an exploded view of our spritesheet with the single sprite we are interested bounded in blue.

GFX2

 

Let's take a look at the code required to load our sprites and display a single one.

 

 

 

package gfs;

import nme.Assets;
import nme.display.Bitmap;
import nme.display.Sprite;
import nme.display.Tilesheet;
import nme.events.Event;
import nme.geom.Rectangle;
import nme.Lib;
import nme.text.TextField;
import nme.text.TextFormat;

class Main extends Sprite
{
	public function new()
	{
		super();
		var sprites:Tilesheet = new Tilesheet(Assets.getBitmapData("img/mechwarrior.png"));
		sprites.addTileRect(new Rectangle(0, 0, 90, 80));
		
		var tileData = new Array<Float>();
		tileData = [0, 0, 0];
		sprites.drawTiles(graphics, tileData);
	}
	
	public static function main()
	{
		// static entry point
		Lib.current.stage.align = nme.display.StageAlign.TOP_LEFT;
		Lib.current.stage.scaleMode = nme.display.StageScaleMode.NO_SCALE;
		Lib.current.addChild(new Main());
	}
}

 

 

Instead of creating a bitmap, we instead create a nme.display.Tilesheet, once again using Assets to load the image file.  Next we the position of our sprite/tile in the sheet.  This is done by calling addTileRect and passing in a rectangle, which are the coordinates within the sheet that our individual sprite.  In this case, at 0,0 (top left), 90 wide and 80 pixels tall.

Next up we create a Float array called tileData.  This one will make a bit more sense in a second.  The array is basically composed of x then y position on screen where you want to draw the tile, followed by the index of the rect in the Tilesheet.  Again, this will make more sense in the future… for now just realize that we are saying to draw the 0th (first) tile in the tile sheet to the position 0,0 on screen.  That is exactly what we do when we call drawTiles.  This is optimized to make drawing tiles from the same sheet as fast as possible by minimizing the OpenGL overhead, something often referred to as sprite batching.

We run the application and we see:

GFX3

 

Our single tile from our tile sheet is rendered in the top left corner of the screen.  Again, this code runs on every platform I tested it.

 

Now drawing a single graphic from a tilesheet is all nice and good, but not really useful.  Often what you are going to want to do is draw an entire level composed of tiles.  Let's take a look how that works ( still using our mechwarrior tilesheet )

 

 

package gfs;

import nme.Assets;
import nme.display.Bitmap;
import nme.display.Sprite;
import nme.display.Tilesheet;
import nme.events.Event;
import nme.geom.Rectangle;
import nme.Lib;
import nme.text.TextField;
import nme.text.TextFormat;

class Main extends Sprite
{
	public function new()
	{
		super();
		var sprites:Tilesheet = 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));
		}
		
		tileData = [
			0,   0,  0,
			90,  0,  1,
			180, 0,  2,
			270, 0,  3,
			360, 0,  4,
			450, 0,  5,
			540, 0,  6,
			0,   80, 0,
			90,  80, 1,
			180, 80, 2,
			270, 80, 3,
			360, 80, 4,
			450, 80, 5,
			540, 80, 6,
			
			];
						
		sprites.drawTiles(graphics, tileData);
	}
	
	public static function main()
	{
		// static entry point
		Lib.current.stage.align = nme.display.StageAlign.TOP_LEFT;
		Lib.current.stage.scaleMode = nme.display.StageScaleMode.NO_SCALE;
		Lib.current.addChild(new Main());
	}
}

 

 

The code is remarkably similar to before, except this time we are creating a number of tile rectangles within our tilesheet.  We are going to create a rectangle for all of the sprites in the first column of our sprite sheet, as shown:

GFX4

 

This time we are going to draw two rows worth of sprites on our screen, so we have a much more complicated tileData array, but it follows exactly the same logic as before:

0, 0, 0,
90, 0, 1,
180, 0, 2,
270, 0, 3, //snip

Considering the above snippet from the array for example, what we are saying is draw the tile at index 0 in the tilesheet to 0,0 on screen, then the tile at index 1 to to 90x,0y then the tile 3rd tile to 180,2, etc… if you were drawing a level, you would obviously have a much larger array.

We run this code and:

GFX5

 

Pretty cool, that's pretty much all you need to know to draw a simple tile based background.  Once again, this code works just fine on every platform I tested.  You may be wondering at this point what determines your screen resolution?  The answer is once again, the application.nmml file.  If you look in yours, you should see something similar to:

<window background="#000000" fps="60" />
<window width="800" height="480" unless="mobile" />
<window orientation="landscape" vsync="true" antialiasing="0" if="cpp" />

This is saying make our window resolution 800x480 unless mobile ( in which case it will be the device resolution ), we set some additional settings if the target is cpp ( iOS, Android, Mac and Windows are ).

 

Since we have a sprite sheet full of animations, let's take a look at doing a simple animation!

 

 

 

package gfs;

import nme.Assets;
import nme.display.Bitmap;
import nme.display.DisplayObject;
import nme.display.Sprite;
import nme.display.Tilesheet;
import nme.events.Event;
import nme.geom.Rectangle;
import nme.Lib;
import nme.text.TextField;
import nme.text.TextFormat;
import nme.events.TimerEvent;


class Main extends Sprite
{
	private var currentFrame: Int = 0;
	public var timer:nme.utils.Timer;
	private  var sprites: Tilesheet;
	
	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));
		}
		
		sprites.drawTiles(graphics, [0,0,0,6], false, Tilesheet.TILE_SCALE);
		timer = new nme.utils.Timer(100);
		
		timer.addEventListener(TimerEvent.TIMER, onTimerTick);
		
		timer.start();
	}
	
	public function onTimerTick(e:TimerEvent): Void {
		graphics.clear();
		if (++currentFrame == 11) currentFrame = 0;
			sprites.drawTiles(graphics, [0, 0, currentFrame, 6], false, Tilesheet.TILE_SCALE);
	
	}
	public static function main()
	{
		// static entry point
		Lib.current.stage.align = nme.display.StageAlign.TOP_LEFT;
		Lib.current.stage.scaleMode = nme.display.StageScaleMode.NO_SCALE;
		Lib.current.addChild(new Main());
	}
}

 

At this point most of the code should be quite familiar.  We create tiles for each frame of animation from the left column of the tilesheet, which represents a mech walk animation. One thing that might look kinda odd is our tileData.  Once again we are only drawing a single tile at the location 0,0, but you may notice there is a 4th value [0,0,0,6].  If you notice in the drawTiles call we pass the flag Tilesheet.TILE_SCALE, that is telling  drawTiles that we will be passing a 4th value representing the amount to scale by ( 6x ).  There are a number of additional parameters you can pass drawTiles, here is an excerpt from the docs:

You can also set flags for TILE_SCALE, TILE_ROTATION, TILE_RGB and TILE_ALPHA.
Depending on which flags are active, this is the full order of the array:
x, y, tile ID, scale, rotation, red, green, blue, alpha, x, y ...

So, for each flag you set, you have to pass an additional value in the float array you pass in to drawTiles.  Next up we create a timer that will fire ever 100 milliseconds.  We then create an event listener that will fire when the timer "ticks" calling the method onTimerTick.  Finally we start the timer.  In onTimerTick, each frame of animation we clear the screen and draw the next frame of animation  by incrementing the index within of the tile we want drawn by drawTiles.  If our animation exceeds the number of frames, we roll back to 0.

 

We run this code and:

Mechwalkcycle

 

Pretty cool!  This one however didn't work on every platform. :(  The timer event doesn't work on HTML.  Unfortunately the timer is only called once instead of every 100milliseconds.  This kind of stuff is rather troubling if you are trying to support multiple platforms at once, especially if you don't know Javascript well enough to troubleshoot the problem!  The key with Haxe development seems to be test early, often and on every platform you want to target.


EDIT: This bug has been fixed in the github version of NME as of April 24/th 2013.  So hopefully it will no longer effect you!

 

Fortunately there is an easy work around for this particular problem.  There is a timer class in the base Haxe library you can use.

 

 

 

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));
	}
	
	sprites.drawTiles(graphics, [0,0,0,6], false, Tilesheet.TILE_SCALE);
	
	timer = new Timer(100);
	
	timer.run = function(){
	graphics.clear();
	if (++currentFrame == 11) currentFrame = 0;
		sprites.drawTiles(graphics, [0, 0, currentFrame, 6], false, Tilesheet.TILE_SCALE);
	}
}

 

With the exception of one thing not working cross platform, I am pretty impressed by Haxe/NME's performance in the graphics category.  Rendering bitmaps, tile sheets and performing frame based animation is all rather simple. Moving on we will next look at handling input with Haxe and NME.

The next part on handling input is now online.

 


17. April 2013

 

With any project, you've got to start at the very beginning and that is exactly what I am going to do with Haxe.  First we are going to create the seminal Hello World and explore the cross platform nature of Haxe.

 

I originally worked in FlashDevelop to author these examples.  I ran into a couple gotchas as I went.  First, I discovered that package names need to start with a lower case character, but FlashDevelop will allow you to make this mistake.  On top, packages are more like Java, than C# or C++ namespaces.  Behind the scenes, they are mirrored on the file system.  So a package mike.is.great becomes /mike/is/great on the file system.  So, when renaming my package, I had to change at the file system level too… lesson learned.  Second, the debugger is incredibly fragile in FlashDevelop, if it goes on the fritz, restart FlashDevelop.  Finally, for some bizarre reason, you can't set a watch on a non local variable.  So if you want to watch a global value, make a local reference.  All told though, working in FlashDevelop is pretty pleasant.

 

Alright, back to HelloWorld.  I'll start with a straight Haxe version that runs in the Neko VM:

package gfs;

import neko.Lib;

class Main
{
	
	static function main()
	{
		Lib.println("Hello world");
	}
	
}

Not the most exciting code, but Hello World rarely is. Now we run our application in the Neko VM:

1

 

Nothing too exciting, but that's Hello World.  Now let's look at the Flash version of Hello World:

 

 

package gfs;

import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.Lib;
import flash.text.TextField;

class Main
{
	
	static function main()
	{
		var stage = Lib.current.stage;
		
		// entry point
		var text = new TextField();
		text.text = "Hello world!";
		stage.addChild(text);
	}
	
}

 

And here it is running in Flash:

2

Once again, not the most exciting app, but it's Hello World.

 

That said, it's pretty annoying creating a version for each platform.  For this we will turn to NME.  NME provides a series of libraries that abstract out the different platform capabilities using a consistent set of libraries.  This HelloWorld is a little bit more advanced:

 

 

 

package gfs;

import nme.display.Sprite;
import nme.events.Event;
import nme.Lib;
import nme.text.TextField;
import nme.text.TextFieldAutoSize;
import nme.text.TextFormat;

class Main extends Sprite
{
	public function new()
	{
		super();
		var stage = Lib.current.stage;
		
		var textFormat = new TextFormat();
		textFormat.font = "Courier";
		textFormat.color = 0xff0000;
		textFormat.size = 42;
		
		var text = new TextField();
		text.text = "Hello World!";
		text.x = Lib.current.stage.stageWidth / 2 - 145;
		text.y = Lib.current.stage.stageHeight / 2 - 21;
		text.width = 290;
		
		text.setTextFormat(textFormat);
		Lib.current.stage.addChild(text);
	}
	
	public static function main()
	{
		// static entry point
		Lib.current.addChild(new Main());
	}
}

 

This code is pretty similar, once again, there is a main method.  You may find it a bit strange that Main inherits from Sprite… this is an ActionScript/Flash thing, the library NME models after.  Now the cool part is running it on various platforms, here are the results (in order) of running:

nme test ios -simulator

nme test Flash

nme test html5

 

iOS

3

 

Flash

Hello World Haxe FLash

 

HTML5

HaxeHTML5HelloWorld.png

 

One source, many platforms… have we reached nirvana?

 

Sadly no.  Here is the result of nme test mac:

Hello world haxe mac

 

Ugh, something obviously went wrong.  Coincidentally the same code runs on Windows, so this is a Mac specific problem.  The problem is, this code is implemented in C++, so then you have to jump in to the generated code.  So, what does this generated code look like?  Well, here's some now:

 

 

#include <hxcpp.h>

#ifndef INCLUDED_gfs_Main
#include <gfs/Main.h>
#endif
#ifndef INCLUDED_native_display_DisplayObject
#include <native/display/DisplayObject.h>
#endif
#ifndef INCLUDED_native_display_DisplayObjectContainer
#include <native/display/DisplayObjectContainer.h>
#endif
#ifndef INCLUDED_native_display_IBitmapDrawable
#include <native/display/IBitmapDrawable.h>
#endif
#ifndef INCLUDED_native_display_InteractiveObject
#include <native/display/InteractiveObject.h>
#endif
#ifndef INCLUDED_native_display_MovieClip
#include <native/display/MovieClip.h>
#endif
#ifndef INCLUDED_native_display_Sprite
#include <native/display/Sprite.h>
#endif
#ifndef INCLUDED_native_display_Stage
#include <native/display/Stage.h>
#endif
#ifndef INCLUDED_native_events_EventDispatcher
#include <native/events/EventDispatcher.h>
#endif
#ifndef INCLUDED_native_events_IEventDispatcher
#include <native/events/IEventDispatcher.h>
#endif
#ifndef INCLUDED_native_text_TextField
#include <native/text/TextField.h>
#endif
#ifndef INCLUDED_native_text_TextFormat
#include <native/text/TextFormat.h>
#endif
#ifndef INCLUDED_nme_Lib
#include <nme/Lib.h>
#endif
namespace gfs{

Void Main_obj::__construct()
{
HX_STACK_PUSH("Main::new","gfs/Main.hx",13);
{
	HX_STACK_LINE(14)
	super::__construct();
	HX_STACK_LINE(15)
	::native::display::Stage stage = ::nme::Lib_obj::get_current()->get_stage();
		HX_STACK_VAR(stage,"stage");
	HX_STACK_LINE(17)
	::native::text::TextFormat textFormat = ::native::text::TextFormat_obj::__new(null(),null(),
null(),null(),null(),null(),null(),null(),null(),null(),null(),null(),null());		
HX_STACK_VAR(textFormat,"textFormat");
	HX_STACK_LINE(18)
	textFormat->font = HX_CSTRING("Courier");
	HX_STACK_LINE(19)
	textFormat->color = (int)16711680;
	HX_STACK_LINE(20)
	textFormat->size = (int)42;
	HX_STACK_LINE(22)
	::native::text::TextField text = ::native::text::TextField_obj::__new();		
HX_STACK_VAR(text,"text");
	HX_STACK_LINE(23)
	text->set_text(HX_CSTRING("Hello World!"));
	HX_STACK_LINE(24)
	text->set_x(((Float(::nme::Lib_obj::get_current()->get_stage()
->get_stageWidth()) / Float((int)2)) - (int)150));
	HX_STACK_LINE(25)
	text->set_y(((Float(::nme::Lib_obj::get_current()->get_stage()
->get_stageHeight()) / Float((int)2)) - (int)21));
	HX_STACK_LINE(26)
	text->set_width((int)300);
	HX_STACK_LINE(28)
	text->setTextFormat(textFormat,null(),null());
	HX_STACK_LINE(29)
	::nme::Lib_obj::get_current()->get_stage()->addChild(text);
}
;
	return null();
}

Main_obj::~Main_obj() { }

Dynamic Main_obj::__CreateEmpty() { return  new Main_obj; }
hx::ObjectPtr< Main_obj > Main_obj::__new()
{  hx::ObjectPtr< Main_obj > result = new Main_obj();
	result->__construct();
	return result;}

Dynamic Main_obj::__Create(hx::DynamicArray inArgs)
{  hx::ObjectPtr< Main_obj > result = new Main_obj();
	result->__construct();
	return result;}

Void Main_obj::main( ){
{
		HX_STACK_PUSH("Main::main","gfs/Main.hx",33);
		HX_STACK_LINE(33)
		::nme::Lib_obj::get_current()->addChild(::gfs::Main_obj::__new());
	}
return null();
}


STATIC_HX_DEFINE_DYNAMIC_FUNC0(Main_obj,main,(void))


Main_obj::Main_obj()
{
}

void Main_obj::__Mark(HX_MARK_PARAMS)
{
	HX_MARK_BEGIN_CLASS(Main);
	super::__Mark(HX_MARK_ARG);
	HX_MARK_END_CLASS();
}

void Main_obj::__Visit(HX_VISIT_PARAMS)
{
	super::__Visit(HX_VISIT_ARG);
}

Dynamic Main_obj::__Field(const ::String &inName,bool inCallProp)
{
	switch(inName.length) {
	case 4:
		if (HX_FIELD_EQ(inName,"main") ) { return main_dyn(); }
	}
	return super::__Field(inName,inCallProp);
}

Dynamic Main_obj::__SetField(const ::String &inName,const Dynamic &inValue,bool inCallProp)
{
	return super::__SetField(inName,inValue,inCallProp);
}

void Main_obj::__GetFields(Array< ::String> &outFields)
{
	super::__GetFields(outFields);
};

static ::String sStaticFields[] = {
	HX_CSTRING("main"),
	String(null()) };

static ::String sMemberFields[] = {
	String(null()) };

static void sMarkStatics(HX_MARK_PARAMS) {
	HX_MARK_MEMBER_NAME(Main_obj::__mClass,"__mClass");
};

static void sVisitStatics(HX_VISIT_PARAMS) {
	HX_VISIT_MEMBER_NAME(Main_obj::__mClass,"__mClass");
};

Class Main_obj::__mClass;

void Main_obj::__register()
{
	Static(__mClass) = hx::RegisterClass(HX_CSTRING("gfs.Main"), hx::TCanCast< Main_obj> ,
sStaticFields,sMemberFields,
	&__CreateEmpty, &__Create,
	&super::__SGetClass(), 0, sMarkStatics, sVisitStatics);
}

void Main_obj::__boot()
{
}

} // end namespace gfs

 

You can read it, but barely. So when the cross platform part of NME breaks down, you are in for a world of hurt. That such a simple example breaks doesn't bode well for the future.

 

In the next post we look at graphics in Haxe and NME.


15. April 2013

So last week I decided to run a poll to see which gaming technology people would be most interested in and the results actually shocked me:

 

 

Haxe narrowly edged out LibGDX ( by two votes ), while my original plan of HTML5 came in a distant third.  The other category seemed to be mostly composed of people interested in MonoGame.

 

I have long been a fan of C# and XNA, so Monogame was an obvious option.  It was ( and is ) discounted for a couple reasons.  First is the inability to target the web.  It's a shame Microsoft put a bullet in Silverlight, as otherwise this limitation wouldn't exist.  Second, paying 300$ to deploy on iOS and another 300$ to deploy on Android is a bit steep.  LibGDX suffers this to a degree too, as you need Xamarin to target iOS.  Hopefully in time this changes.

 

Hello Haxe World

So over the last couple days I've taken a closer look at Haxe or more specifically Haxe + NME.  Haxe as a programming language is an interesting choice.  It has an ActionScript like syntax and compiles to it's own virtual machine like Java ( the VM is called Neko, so if you are wondering what Neko is…  well, now you know!)  If that was all Haxe did, we wouldn't be having this conversation.  More interestingly, it compiles to a number of other programming languages including HTML5 ( JavaScript ), ActionScript ( Flash ) and C++.  As a result, you can compile to C++ for Android and iOS and get native performance.  This makes Haxe quite often many times faster than ActionScript.

 

There is however a serious problem or flaw with Haxe.  Let's take a quick look at part of the Haxe API and you will see what I mean:

 

Haxe Api

 

Notice how there are a number of language specific apis, such as cpp, which I've expanded above.  So, what happens if you use the cpp Random api and want to target Flash? Short answer is, you can't.  If course you could right a great deal of code full of if X platform, do this, otherwise do that, but you will quickly find yourself writing the same application for each platform.  So, what do you do if you want to write for multiple platforms with a single code base?

 

Meet NME

This is where NME comes in.  NME builds on top of the Haxe programming language and provides a Flash like API over top.  Most importantly, it supports Windows, Mac, Linux, iOS, Android, BlackBerry, webOS, Flash and HTML5 using a single code base.  Let's take a quick look at part of the NME libraries to show you what I mean:

NME api

 

There are still platform/language specific libraries, like the neko.vm above.  There are also the nme.* libraries.  These provide a Flash like API for the majority of programming tasks needed to make a game.  Targeting your code towards these libraries, and minimizing the use of native libraries, and you can target a number of different platforms with a single code base.

 

There is another aspect to NME that makes life nice.  If you read my review of the Loom Game Engine you may recall I was a big fan of the command line interface.  NME has a very similar interface.  Once you've installed it, creating a new project from one of the samples is as simple typing nme create samplename then you can test it by running nme test platform.

 

Here is an example of creating the DisplayingABitmap sample and running it as an HTML5 project:

Creating an NME project

 

And your browser will open:

NME running as HTML5

 

What makes this most impressive is when you target C++, it fires off the build for you using the included tool chain.  Configuring each platform is as simple as nme setup platformname, valid platforms are currently windows, linux, android, blackberry and webos.  Unfortunately Xcode can't be installed automatically, so this process won't work for iOS or Mac, you need to setup Xcode yourself.  As you can see, setting up and running NME/Haxe is quite simple, and worked perfectly for me out of the box.  If you are curious what options you can provide for sample names, you can get them from the Github directory.  There are a fair number of samples to start with.

 

Now is when things take a bit of a turn for the worse…  getting an IDE up and running.  This part was a great deal less fun for me.  You can get basic syntax highlighting and autocompletion working on a number of different IDEs, here is the list of options.  This is when things got rather nasty.  I was working on MacOS so this presented a bit of a catch.  I tried getting IntelliJ to work.  Adding the plugin is trivial ,although for some reason you need to use the zip version, it's not in the plugin directory.  Configuring the debugger though… that's another story.  I spent a couple hours googling, and sadly only found information a year or two old.

 

Then I tried MonoDevelop, there is a plugin available and its supposed to be a great option on MacOS.  And….  MonoDevelop 3 is no longer available…  It's now Xamarin Studio 4 and the plugin doesn't work anymore.  Good luck getting a download for MonoDevelop 3!  There is also FDT, which I intend to check out, but it's built on top of Eclipse and I HATE Eclipse.  Eventually I got IntelliJ Flash debugging to work but it was a great deal less fun.

 

After this frustrating experience, I rebooted into Windows and things got a TON better.  FlashDevelop is easily the best option for developing with Haxe but sadly it's only available on Windows.  There was however a major catch… debugging simply did not work.  After some digging, it turns out you have to run the 32bit JDK or it doesn't work.  Seriously, in this day and age, still having Java VM problems like this is just insane.  Once I got that licked, I was up and running.

 

At this point I have a working development environment up and running I can get to coding.  If you are working on Windows, using FlashDevelop you can get up and running very easily, so long as you are running 32bit Java.  On MacOS though, expect a much rockier experience.  It would be great if FlashDevelop could be ported to Mac, but apparently it can't be… there have been a number of attempts.  They have however provided a configuration for working in Virtualized settings ( VMWare, Parallels, etc ).

 

Stay tuned for some code related posts soon.


AppGameKit Studio

See More Tutorials on DevGa.me!

Month List