What We Use
This post is intended as a guide to help new users of HaXe get setup with HaXeFlixel and understand how the HaXeFlixel demos are setup. Hopefully this will explain some of the curious things you might see in the demo files and help you prepare to make use of them!Technologies used in this post:
- FlashDevelop
- HaXe
- HaXeFlixel
A Note About Terminology
I've bounced around through enough programming languages in my life that I use similar terms in different languages interchangeably. You'll notice sometimes I call them methods, sometimes I call them functions. Hell, I'll even call them subroutines if I'm feeling froggy. I use the terms package and library interchangeably too. Please excuse me if I use terms wildly and freely. I promise I'll use a term that's appropriate for some language somewhere most of the time. It entirely depends on what's going through my head at any given moment and what randomly pops out.Install Needed Tools
Setup FlashDevelop first since that's our IDE. Here are the instructions to download and install FlashDevelop, HaXe, and HaXeFlixel:Get FlashDevelop
Get HaXe
Get HaXeFlixel
We install HaXeFlixel so we have a standard, well-developed game framework. OpenFL gets installed as a dependency of HaXeFlixel. OpenFL is an excellent cross-platform software aimed at using graphics hardware instead of loading all the work on the CPU (this works well with mobile platforms such as iOS, Android, and Blackberry). It's an excellent tool for developing high performance Flash games too because it mimics how Stage3D works. It's also a great tool for developing desktop applications aimed at Windows, Mac, and Linux because it's essentially an open port of OpenGL. This combination of technologies is like the Swiss army knife of software development!
Installing Community Libraries
There are a large number of well developed HaXe Community Libraries available through haxelib. Some of the demo's on HaXeFlixel depend on community libs. Let's install some of those libs since you will need them to compile the HaXeFlixel demos. Run these commands from a command prompt:haxelib install flixel
haxelib install systools
haxelib install task
haxelib install nape
haxelib install openfl
haxelib install lime-tools
haxelib install lime
haxelib install flixel-tools
haxelib run lime setup
haxelib run flixel-tools setup
Installing Packages from Git
At some point you'll want to install a package that's not a HaXe community lib. One of the demos on the HaXeFlixel site (RPG Interface) uses a package called firetongue; this is not available as a community lib. Here's the syntax to install a git repo lib:haxelib git package-name git-url
I'm not saying you need to install this package but this is how you would install firetongue from git if you want it. You need to install the git client to do this (run git commands from the Git Shell on Windows, not the command prompt!):
haxelib git firetongue https://github.com/larsiusprime/firetongue
As an alternative to installing a package you can download it and place it directly in your project's source folder. If you do this and your project config file calls for the package to be installed you'll need to comment that line out. In the root of your FlashDevelop HaXe project folder there's a file called Project.xml:
<?xml version="1.0" encoding="utf-8"?>
<project>
<app title="...">
...
<haxelib name="flixel"/>
<haxelib name="firetongue"/>
<!--haxelib name="firetongue"/-->
You only need to do this if you put the library directly in the source folder instead of installing it.
Update to the Latest Version
We want to use the latest version of HaXe, HaXeFlixel, and all our HaXe libraries. For anyone who's worked in a Linux/Unix environment this will look really familiar. Run this command from a command prompt (if you install libs from Git you'll need to use the Git Shell):haxelib upgrade
Now our libraries are up to date. Let's update haxelib (the HaXe package manager):
haxelib --global update haxelib
haxe upgrade.hxml
We're all up to date! For a list of haxelib commands you can type
haxelib
from the command prompt. You can also view the haxelib reference page.HaXeFlixel Demo Breakdown
Now that we're setup, let's look through the basic structure of a HaXeFlixel demo to get familiar with what's going on. In this section you'll see snippets of code in HaXe. Where appropriate I'll explain how they relate to other languages like AS3, Java, and C++.
The structure for all the HaXeFlixel demos is similar so it doesn't matter which one you choose. Pick one you like and open it with FlashDevelop. I chose FileBrowse because it's fairly simple and small. Start by opening Main.hx:
package;
...
import flash.Lib;
...
class Main extends Sprite
{
// Entry point
public static function main():Void
{
Lib.current.addChild(new Main());
}
...
}
The main() method is the entry point where our program begins execution. The line
Lib.current.addChild(new Main());
is an extension of the flash package. If you didn't want to include the flash package you would rewrite this as:flash.Lib.current...
This line adds an instance of the Main class as a new object to the Flash stage. For those familiar with AS3 this will make sense. For those familiar with C++ and Java, the Flash stage is a display object handler with a lot of useful features for manipulating images and vector graphics. This is a required component for all HaXeFlixel applications. You should get familiar with how this works in AS3 because you'll use it a lot.
You might find yourself asking, "if the entry point of my program starts in the main() function, does that mean making a new object from the Main class will call main() again?" This is a legitimate question and it confused me at first. Java behaves something like this so if you come from a Java background you likely just asked yourself that question.
With HaXe, you write the main() method only for the class where program execution begins. You can write a main method for other classes but it'll never run unless you explicitly call it. When you make a new object from a class in HaXe it executes the new() function. Let's look at that:
class Main extends Sprite
{
...
public function new()
{
super();
if (stage != null)
{
init();
}
else
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
}
...
}
This is what executes when we add Main to the stage as a new object. In the if block it checks to make sure the stage exists first (this is based on Flash behavior). In Flash, it's not guaranteed the stage will exist when you add something to it. Extremely odd idea but that really is how it works. The else block adds an event listener to the stage that fires the function init() once the stage does exists.
If you're not familiar with event handlers in Flash you can think of them similar to throwing an exception that will not stop execution or cause an error; the stage throws the exception and all child objects receive a copy. If a child of stage throws an object then it will bubble up through all parent objects including the stage. The event is handled silently by the runtime environment so it doesn't stop execution. It just throws an information object out for anyone listening so they know when something happens. There are loads and loads of different events in Flash. You can even extend the Flash native Event class to create your own custom events. Events in Flash are somewhat slow but can be useful at times. I stay away from events because they can cause serious memory leaks if not handled carefully.
Let's look through the init function:
private function init(?E:Event):Void
{
if (hasEventListener(Event.ADDED_TO_STAGE))
{
removeEventListener(Event.ADDED_TO_STAGE, init);
}
initialize();
var game:FlxGame = new GameClass();
addChild(game);
}
This method is fairly straightforward so I'll focus on the critical pieces. You probably noticed the ?E:Event as part of the function declaration and wondered what the heck that means. Adding a ? before a variable as part of a method declaration makes it optional; very useful for cases where you don't care if something gets passed or not. This saves creating a null object as a default value for a variable you don't care about and don't need. Good practice to use when you can.
The initialize() method is fairly simple and easy to understand. It sets display characteristics so I won't bother explaining it.
The variable game has a type of FlxGame and stores a newly created GameClass() object. It then gets added to the stage after it runs it's constructor. This is where we're going next. Open the file GameClass.hx:
...
class GameClass extends FlxGame
{
public function new()
{
var stageWidth:Int = Lib.current.stage.stageWidth;
var stageHeight:Int = Lib.current.stage.stageHeight;
var ratioX:Float = stageWidth / 640;
var ratioY:Float = stageHeight / 480;
var ratio:Float = Math.min(ratioX, ratioY);
var fps:Int = 60;
super(Math.ceil(stageWidth / ratio), Math.ceil(stageHeight / ratio), PlayState, ratio, fps, fps);
}
}
This whole class consists of setting up the display, frame rate, and passing a class name to the Flixel engine via extending the FlxGame class and calling super(). For those familiar with AS3 this would be a nightmare to get working because you'd need to call super() as the very first line of the constructor. You can imagine trying to do all of that math crunching on one line inside the super() parenthesis... I don't want to imagine what that would look like. Thankfully you don't have that superficial restriction in HaXe; you can call super() anywhere in the constructor.
Now that we've gotten this far we have a Flixel engine setup and it has the class name PlayState passed to it. Let's open PlayState.hx and see what we find:
...
class PlayState extends FlxState
{
... variable declarations
override public function create():Void
{
...
}
}
Whoa, buddy! Where's the new() function? Flixel does create a new PlayState object but does not require an override constructor method. Instead, it relies heavily on create() and destroy() so it can run object pools! I'll explain object pools in another post but for now understand this allows an object to be reused. Mobile platforms and Flash take a lot of CPU power to make new objects so it's best practice to create many up front and use them over and over. Object pools work well for desktop applications because you don't make a new object every time you need it; you ask the object pool for one that already exists. Object creation is automatically handled, memory space already allocated, and you don't end up with as many weird memory bloat issues when you use object pools.
This is where we stop for now because going further requires explaining how the Flixel camera system works and more about the Flixel framework in general. I have a series of posts planned to show you how to make a platformer infinite runner style game later so you'll just have to wait and see. If you want some further reading I would suggest these links:
http://haxe.org/doc
http://api.haxeflixel.com/
http://haxeflixel.com/documentation/handbook/