Forewords
Libgdx is a stable 2D/3D library, quite complete, multiplatform (Desktop PC/Mac/Linux, Android, iOS, HTML, Blackberry?), performant, easy to grasp and it comes with useful tools, a clean documentation and a decent community.
There are two problems: java and… well… java
I don't hate java, it's just that it doesn't reach the top 3 of my favorite languages :)
Haxe can compile to jar and java source code hence replacing java with haxe should be doable. The haxe/java support is quite new so things may break but givin a try doesn't hurt!
With the latests releases libgdx now comes with a new build/dependency system based on gradle. This tool eases many things and provides a nice and simple access to compilation tasks from the command line.
The project structure
I chose to isolate haxe from libgdx projects but you are free to customize to your needs.
mygame/
gdx/ # libgdx content
src/ # our haxe source code will go there
build.hxml # our build script (could be a Makefile)
The workflow:
- we code in haxe in the src folder
- haxe compiles the code to java source and send it into gdx/
- we use gradle to compile the gdx java project
So for now:
$ mkdir -p mygame/src
$ mkdir -p mygame/gdx
$ touch mygame/build.hxml
We will edit build.hxml later.
Create the gdx project
Libgdx has a setup tool which eases the creation of new projects:
Download and run gdx-setup.jar (
documentation)
java -jar gdx-setup.jar
Just make sure to choose "mygame/gdx" for the destination and to point to the right path of your android-sdk if you plan to play with Android. (on macosx I downloaded the android-sdk with brewer).
Click Generate and wait a little.
Ensure that the gdx base project works
$ cd mygame/gdx
$ ./gradlew desktop:run
You should see something like that:
GDX organisation
If you are not familiar with libgdx you can take a look at their excellent wiki.
The gdx/ structure looks like this
core/
android/
desktop/
ios/
html/
Plus some project files.
Each folder is a subproject with a java launcher dedicated to one platform. The launcher initiates what's inside "core" : our game.
Note: for some reason games assets must be put into android/assets
by default but you can change this editing the files desktop/build.gradle
, html/src/me/labe/mygame/GdxDefinition.gwt.xml
, html/src/me/labe/mygame/GdxDefinitionSuperdev.gwt.xml
,
ios/robovm.xml
(fgrep -R "android/assets" *
is you friend).
Gradle is a dependency/build system, some interesting build tasks:
$ cd gdx
$ ./gradlew desktop:run
$ ./gradlew android:build android:installDebug android:run
$ ./gradlew ios:build ios:launchIPhoneSimulator
And many other things we don't care about right now :)
So now that you have everything running (use libgdx documentation to fix things until the default project works on your system) we can start using haxe.
MyGame.hx file
Gdx-setup created core/src/me/labe/mygame/MyGame.java
for us, it looks like this:
package me.labe.mygame;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
public class MyGame extends ApplicationAdapter {
SpriteBatch batch;
Texture img;
@Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
}
@Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(img, 0, 0);
batch.end();
}
}
This is the regular entry point for our game so we want it to be written in haxe.
Here's the haxe equivalent: src/me/labe/MyGame.hx
package me.labe.mygame;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
// this is important, in haxe you cannot access java interfaces' static members,
// they are hidden inside a subclass CLASSNAME_Statics, must know trick, hope
// it will be fixed soon if not already fixed.
import com.badlogic.gdx.graphics.GL20.GL20_Statics.*;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
class MyGame extends ApplicationAdapter {
var batch : SpriteBatch;
var img : Texture;
@:overload
public function create() {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
}
@:overload
public function render() {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL_COLOR_BUFFER_BIT);
batch.begin();
// we center the image so we know for sure OUR code is in charge
batch.draw(
img,
(Gdx.graphics.getWidth() - img.getWidth()) / 2,
(Gdx.graphics.getHeight() - img.getHeight()) / 2
);
batch.end();
}
}
We want haxe to compile this to java and replace the original gdx file. It's quite easy.
build.hxml
Our first iteration looks like this:
-D no-compilation
-cp src
-java gdx/core
me.labe.mygame.MyGame
We don't want to compile jar files, we just want java source.
Our haxe code resides in the src folder.
We send the result into gdx/core as expected.
Our entry point is me.labe.mygame.MyGame.
If you try to run this build script with haxe you will get an error:
src/me/labe/mygame/MyGame.hx:3: characters 7-42 : Class not found : com.badlogic.gdx.ApplicationAdapter
That's because haxe doesn't know about gdx yet, we have to tell it about the .jar files we are using.
Link the libs
Since we compiled the gdx project, the jar files should already be somewhere on our system.
We are looking for gdx.jar
, gradle uses ivy for its dependencies. It caches things inside the ~/.ivy2
folder on OSX.
With OSX or linux you can find your libs with a command like this:
$ find ~/ -name "*.jar" | grep gdx.jar
...
/Users/lbedubourg/.ivy2/cache/com.badlogicgames.gdx/gdx/jars/gdx-1.2.0.jar
...
As seen in the above gdx-setup screenshot, 1.2.0 is the right version, we can use this full path, create a symlink, even copy the file, whatever you prefer. I'll use the full path for this tutorial.
We just have to add the reference to the lib to our build.hxml:
-D no-compilation
-cp src
-java gdx/core
-java-lib /Users/lbedubourg/.ivy2/cache/com.badlogicgames.gdx/gdx/jars/gdx-1.2.0.jar
me.labe.mygame.MyGame
And voila, haxe build.hxml
will generate your java code.
Enjoy
Running our code using gradle is just a question of going into the gdx folder and running ./gradlew with the right arguments.
$ cd gdx
$ ./gradlew desktop:run
You will certainly want to include the gradlew call into your build.hxml or your Makefile or whatever you are using to manage your build process.
For instance to compile and run the desktop version after each compilation you can edit the build.hxml file:
-D no-compilation
-cp src
-java gdx/core
-java-lib /Users/lbedubourg/.ivy2/cache/com.badlogicgames.gdx/gdx/jars/gdx-1.2.0.jar
me.labe.mygame.MyGame
--next
-cmd cd gdx
-cmd ./gradlew desktop:run
Next
You'll want to learn a little bit about gradle.
- libgdx uses gradlew (a gradle wrapper) and on my macbook using the wrapper as instructed in this tutorial was slower than using my localy installed gradle (version 1.12).
- using
--offline
can save you a few networking roundtrip and one or two seconds while starting gradle - libgdx requires old gradle, things may improve when they will switch gradle v2.0, maybe :)
HTML support doesn't work out of the box.
Libgdx relies on GWT to generate javascript code, you will have to edit gdx/core/src/MyGame.gwt.xml
to add the following lines (so GWT will find haxe stuff).
<source path="haxe"/>
<source path="haxe/lang"/>
<source path="haxe/root"/>
<source path="_Array"/>
But then gwt will complain about java.lang.reflect.* which is not supported by GWT… This problem might be fixed by this pre-processor but I don't have the time to investigate more.
Replacing platforms launchers with haxe should also possible, you will have to create different targets and add the related jar file as -java-lib.
You can use libgdx extensions, don't forget to add -java-lib to make haxe happy.
I just made it work yesterday and did nothing more than what is written here, moreover haxe/java support is quite new, so bugs may arise if you try to use libgdx with haxe to produce an entire game.
Haxe is great because you can reuse the hard work of others. Of course having a great multiplatform 2d/3d api entirely written in haxe would be awesome but in the meantime, using libgdx could be a great alternative to adobe air. It's up to you to decide, you now have one more choice!
Best regards, Laurent
EDIT: As written, haxe/java support is quite recent and a few bugs needs to be resolved before we can use all of GDX features from haxe. However the bases are there and I am pretty sure the haxe/java team will be happy to clean edge cases.
More in part 2.