Pages

Sunday, March 30, 2014

Developing a cross-platform game with Haxe, OpenFL, and HaxePunk

In a previous post, I announced the release of Gorilla Defense for Android, which was developed using the Haxe programming language. I've seen Haxe and OpenFL getting some social media attention lately, and people may be wondering what the process of developing a cross-platform game is really like and whether Haxe lives up to the hype. Here's a little technical writeup detailing my experience.

Gorilla Defense is a quick and fun tower defense game. It's available for Android (Google Play, Amazon) as well as Flash (Kongregate) and desktop (not yet released). I'd love to release iOS, BlackBerry, and Windows Phone versions but I don't currently have any of those devices - hopefully coming soon, though.

In short, I was able to develop the core game in three days, and have added roughly an additional week of development time for polish and integrating some Android SDKs. Part of the reason I'm able to work so fast is because I've been working with Haxe long enough to have a good grasp of the technical ecosystem. I know what it's capable of and what's going to be easy, I can use previous projects as samples, and use this knowledge to guide design decisions from the very beginning. The other part is the incredible tool itself - you'll see that deciding to make Gorilla Defense cross platform actually involved very little additional work on my part due to the maturity of Haxe, OpenFL, and HaxePunk.


Development



The idea came suddenly: I had been wanting to develop a quick tower defense game, and somehow found myself remembering the old "Gorillas" DOS game. The two ideas had a whirlwind romance in my head and their child Gorilla Defense was born.


Graphics


I planned Gorilla Defense as a cross-platform game from the start, so I knew I'd want device-independent graphics. I decided to use Inkscape to draw my gorilla character using SVG:


Each body part is an individual Inkscape layer (there's only one leg because the legs are identical.) I used a quick Python script and the Inkscape command line to tool to split out each layer into a PNG image at various resolutions, then used Spine to put together a skeleton and create some basic animations - walking, falling, climbing, just sitting there, and a victory dance. Since this is a workflow I've done several times before and the gorilla above is not exactly a huge feat of technical artistry, the process of creating the art took only a couple of hours. I also made a brown version for the enemy gorillas, and used a Spine skin to swap in all of the brown parts with a single switch.

The cityscape was developed as a tileset in Inkscape and individual city layouts were created using the Tiled map editor. I could've used procedural generation to generate random cityscapes like Gorillas did (actually that's something I'd like to add in a future update), but at this point I was going for development speed and wanted something that just worked without verifying that I was producing only valid skylines.


Putting the prototype together


HaxePunk is one of my favorite game engines. It's incredibly convenient for quick prototype development and is packed with features. It's also open source, but more than that, it's very well engineered. I started contributing to HaxePunk within weeks of starting to use it myself and am comfortable adding features, bug fixes, etc. whenever I need them.

In HaxePunk, you create a "Scene," add Entities for individual game objects, and use the Entity and Scene "update" methods called every frame to handle behavior. Entities have hitboxes and can take care of collision detection for you; I use simple rectangular hitboxes, but more complex varieties exist. During development, it's easy to compile for Flash or Neko (a virtual machine which is much faster to build for than compiling a native C++ application) and pull open a debug console.

The console lets you monitor the framerate, view debugging output, track entities and hitboxes, manually move things around with your mouse until everything looks just right, and more. You can pause, advance one frame at a time, or run at normal speed with the hitboxes shown which is very helpful for debugging any issues that arise.


My Spine animations were exported to JSON; I used spinehaxe (a Spine runtime for Haxe, written by me) and SpinePunk (a spinehaxe rendering engine for HaxePunk, also written by me) to get the gorillas moving. SpinePunk automatically generates hitboxes on a per-body-part basis, so I can define a combination of parts (head, body, legs) for the hitbox and it will track movement in those parts.

The prototype was straightforward to develop and took about 2 days of very part time effort.


Now for the cross-platform part


I did most of my testing on Flash, and at this point I had a functional Flash prototype. How did I go from that to an Android game? Are you ready?

I ran the command "openfl build android."

That's actually all there is to it. There are obviously some major differences between both platforms - rendering, for example. Flash uses a software renderer, which would be painfully slow on mobile devices. Fortunately, HaxePunk and OpenFL take care of these differences behind the scenes. I use standard HaxePunk Graphic objects like "Image," "Tilemap," "Spritemap," and "Emitter," and HaxePunk takes care of the differences in rendering for me. SpinePunk also has two rendering modes, one for software and one for hardware acceleration. This means that during the actual game development, I don't have to worry about these details one bit. I just have to create textures and make sure to use these standard cross-platform Graphic objects.

Fortunately, the game was developed with primarily mouse-based controls, which transfers to a touchscreen device very easily. With OpenFL/HaxePunk, the mouse controls actually translate across to mobile effortlessly - mouseDown is true when the player is touching the screen, mouseX and mouseY give the coordinates of the touch, etc.

When I run "openfl build flash," the Haxe code is cross-compiled into Flash bytecode, with all of the Flash-specific directives built in. When I run "openfl build android," it's cross-compiled to C++ and an APK is built using the Android NDK. Two one line commands produce two very different but visually identical, working versions of the same game with no additional effort on my part.

With one extra line in my project.xml file, I can even sign my APK, so that one line is all that's necessary to build a package that's ready for the app store of your choice.


Small differences


The Haxe language features conditional compilation, so you can include code blocks which are only compiled based on defined variables, such as which platform you're currently targeting, and this was necessary in a few places:
  • The control schemes were slightly different. On Android, I needed to overload the default back button functionality. I also defined menu and back as "pause" and "quit." On Flash these were defined as keys P and Q or Escape. HaxePunk allows giving names to one or more keys and using those names within your code, so the only part that requires a platform-specific block is the initial key definition.
  • Formats of sound files were different - Flash used MP3 for music, while Android used WAV. This affected a single variable (musicSuffix) defined in the Sound class. A Makefile was used to generate both versions of necessary sound assets automatically.
  • Text and assets in a few places needed to be changed - for example, "right click to buy a gorilla" on Flash is "tap and hold to buy a gorilla" on Android. For text, this was simple: #if mobile (some code) #else (some other code) #end. For images, OpenFL's asset manager is fantastic in dealing with these issues. In my project's XML configuration file, with two lines:

    <assets path="assets/help" rename="graphics" unless="mobile" ...
    <assets path="assets/help-mobile" rename="graphics" if="mobile" ...

    ...I was able to keep two separate directories of platform-specific help graphics. Both sets of images would have their paths renamed to "graphics" internally, so in the actual code, I didn't have to deal with the difference at all.
  • Recently I added a "thanks for playing, please review me!" screen that shows after a player has played three times. I created separate builds for Amazon and Google Play which I compile with one different command-line flag (compile with "-Damazon" for Amazon vs. "-Dgoogleplay" for Google Play) which will modify the review URL that is used. I added conditional code to only show this screen on mobile devices.
  • The Flash version uses the Kongregate API for leaderboards. Thanks to the author of kong.hx which is open source and available on GitHub, it took about 15 minutes to implement.


Including native SDKs


I incorporated two Android SDKs into the Android version, one for RevMob (ads) and one for Google Analytics. Both SDKs provide a .jar file and some documentation on how to alter your Android activity to incorporate their functionality.

In OpenFL, default template files are used for your Android activity, manifest, etc. However, it's easy to overload these with your own versions in the project.xml file. You can then add any necessary Java to incorporate the SDKs you need.

Calling native functions from Haxe is fairly simple. Having defined a "showAd" function in my MainActivity, I'd call it from Haxe using the name and JNI method signature:

    showAd = openfl.utils.JNI.createStaticMethod("com.monsterface.gordef.MainActivity", "showAd", "()V");
    showAd();


Conclusion


Haxe and OpenFL are an incredible technology stack that abstract away platform-specific differences and allow you to focus on developing quality games fast. Engines like HaxePunk and HaxeFlixel (and there are plenty of others, which I unfortunately haven't had the opportunity to try out yet) can take your productivity to the next level, providing debugging capabilities and common game and graphics functionality.

Support Haxe and OpenFL in this week's Humble Bundle weekly sale!

Update 4/4: the Flash version of Gorilla Defense has been published on Kongregate.

8 comments:

  1. hi, can you expand on "Including native SDKs"?
    "However, it's easy to overload these with your own versions in the project.xml file." can you point me to the option you've used, etc.?

    ReplyDelete
  2. Sure. When you build for Android, you can see various generated files in bin/android/bin, such as AndroidManifest.xml and MainActivity.java in the src directory. If you want to include additional permissions or SDK methods, these are the two files you'll need to change.

    First, I made a "templates" directory and copied these generated files into it and made my modifications. Then, In project.xml, I used "template" tags, like this:

    <template path="templates/AndroidManifest.xml" rename="AndroidManifest.xml"/>

    to replace the generated files with my custom versions.

    For revmob/Google Analytics, I defined Java static methods in MainActivity which were called using the JNI from Haxe. The Java method would be something simple like:

    public static void showAd() {
    revmob.showFullscreen(mainActivity);
    }


    ...and would be used from Haxe like this:

    showAd = openfl.utils.JNI.createStaticMethod("com.monsterface.gordef.MainActivity", "showAd", "()V");

    showAd();

    ReplyDelete
  3. Waowww Blog! Thanks for Sharing! I want to share Wearable Game. Burj Builder is a superb, physics-based tower building game - only people with quickfire building skills need apply. Get this Wearable Game: Here >>>

    ReplyDelete
  4. Hello,
    The Article on Developing a cross-platform game is nice. It give detail information about it ,Thanks for Sharing the information about Cross Platform.Xamarin Developer

    ReplyDelete
  5. Wow, What a Excellent post. I really found this to much informatics. It is what i was searching for.I would like to suggest you that please keep sharing such type of info.Visit here for Penetration testing services and Software testing services

    ReplyDelete