Written by Michel Gerard.

Disclaimer : I’m not a technical expert. I’m a self-taught curious guy whose dream was to make games. I do all by myself : code, graphics and music. And this is my first time at writing an article, so be kind :)

From flash to libgdx

I started making flash games back in the days (2009) and then progressively switched to mobile games using the libgdx framework. I found Actionscript 3 and Java quite similar so the transition was really smooth. OpenGL and rendering stuff was a bit tough in the beginning but at the end, I was able to port some of my flash games to IOS and Android (10 Bullets, 10 More Bullets, Boomerang Chang,…)

From libgdx to HTML5

Lately, I wanted to give a try to the libgdx HTML5 export. So here is what I had to do to make the HTML5 port of an existing libgdx Android game, Boomerang Chang :

Play the game : Boomerang Chang

The process

The idea behind the whole process was like : keep it simple, avoid the problems, and make it work !

Here are the steps I went through :

Make it compile via GWT. Launch the game on a local server Adapt for desktop vs mobile Leaderboard Loading bar Google analytics Go full screen Prevent special keys input Sounds

1. Make it compile via GWT.

GWT is the tool integrated in libgdx that will compile your Java code to Javascript, making your game playable in any browser.

To get rid of problems and compiling errors I encountered, here is what I did :

I adapted to my package structure and add the main packages to the

list of modules in the myGdxGame.gwt.xml file so that GWT can find

your source code. In my case I have 2 main packages in the core

: app (with the current game code) and pp (with the reusable

code) : In myGdxGame.gwt.xml : <module> <source path="com/crazygames/boomerangchang" /> <source path="app" /> <source path="pp" /> </module> I didn’t use any IDE GWT plugin. I compiled from the comand line : ./gradlew html:dist To do that : open a terminal, navigate to the root folder of your project and write the command ./gradlew html:dist on mac, gradlew html:dist on windows I skipped some Java classes I was using (e.g. java.text.DecimalFormat) I didn’t use relfection. I didn’t bother too much with the debugging or superdev mode. I test the game on desktop and when it works, I compile it. Quick note though : this GWT compilation is a really slow process (5 to 10 minutes)! When it succeeds, you will find your game in the folder : myRootFolder/html/build/dist . The index.html is actually the webpage that contains your game and all the assets.

2. Launch your game on a local server

To easily deploy on my machine via localhost, i used the very useful node.js module « serve ». I installed it via the command npm install -g serve (details on the module installation)

Then, to launch your game, I go to the « dist » folder via the terminal and type the command serve . I can now open a brower at the address http://localhost:5000 and see the game running !

3. Adapt for desktop vs mobile

The main power of HTML5 games is that they are playable everywhere, on desktop or mobile devices. There is thus some work to do to best fit each « platform ».

Boomerang Chang has been developped for mobile first, so it is a « 2 buttons game » that matches the simple left/right screen tap input of a mobile device. You can thus only jump or shoot to the right. On the desktop version, inputs are up and right arrows.

So, here is what I did :

Add an control selection screen at the start of the game. Add the keyboard control. Adapt in-game messages accordingly (« press up arrow » vs « tap left »). Those are simple if else statements at various places in the code.

In this era of big competition, a small marketing effort is necessary :) So you have to maximize your cross-selling tactics, and show your players the way to your Facebook, Twitter, and the like.

The game over screen is a good place to put those links :

To avoid those url links to be blocked from the browsers, the code that opens the url must be directly binded to the user input. So, to make it work, I had to refactor the way some of my ingame buttons worked.

I have used the libgdx InputProcessor to catch the touchUp events. Then, I looped through all my url navigation buttons, and if their hitbox contains the coordinates of the touchUp event, I call an doOpenTheUrl function :

private native void doOpenTheUrl(String s) /*-{ $wnd.open(s, "_blank", ""); }-*/;

This Java function is using JSNI. JSNI allow you to direcly call javascript functions from inside your Java code. Very powerful.

Basically, the $wnd refers to your main « window » in javascript. Then the .open() is basic javascript code that open the link in a new tab.

5. Custom Leaderboard (JSNI + json + PHP)

This was the big part.

For the Android version, I used Google Play Games Services. Everything worked out of the box, scores, achievements, etc.

For the HTML5 version, I decided to use my own leaderboard system (as I did in the flash version of the game) because I find it more flexible and also more enjoyable to develop.

So, the back-end consists of simple web services written in PHP and plugged to a MySQL database. It can save scores and retrieve them via JSON.

Here is how I made the calls :

String url = "http://www.yoursite.com/yourApi/yourServiceName" ; RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url); builder.setHeader("Content-type", "application/x-www-form-urlencoded"); StringBuilder sb = new StringBuilder(); sb.append("yourFirstParameterKey").append("=").append("yourFirstParameterValue").append("&"); sb.append("yourSecondParameterKey").append("=").append("yourSecondParameterValue") ; try { Request request = builder.sendRequest(sb.toString(), new RequestCallback() { @Override public void onResponseReceived(Request request, Response response) { if (200 == response.getStatusCode()) { onResultDoRefreshTheScores(JsonUtils.<JsArray<ScoreData>>safeEval(response.getText())) ; } } @Override public void onError(Request request, Throwable exception) { // handle errors here }}); }catch (RequestException e) { // handle exceptions here } }

That call will trigger the Ajax and call the function onResultDoRefreshTheScores when it receives data back from the back-end.

private void onResultDoRefreshTheScores(JsArray<ScoreData> scores){ LeaderboardScore score ; ScoreData data ; ArrayList<LeaderboardScore> aScores ; aScores = new ArrayList<LeaderboardScore>() ; for(int i=0;i<scores.length();i++){ data = scores.get(i); score = data.getLeaderboardScoreObject() ; aScores.add(score); } // refresh the UI Game.panelLeaderboard().refreshWithScores(aScores); }

Then, I also have to use a class to format the data and build my LeaderboardScore java object based on the JSON coming back from the server. See more info on JSON with GWT .

This is the structure of the score coming back from the server in json :

[{ "nickname":"GUEST", "rank":0, "ago":"6 hours ago", "score":33, "country":"US" }, { "nickname":"GUEST", "rank":1, "ago":"7 hours ago", "score":26, "country":"US" }]

So, to handle the json, i have an intermediate class ScoreData that can make the mapping to the Java object « LeaderboardScore » that I need to fill the leaderboard in the user interface.

public class ScoreData extends JavaScriptObject { protected ScoreData() {} public static int rank, score ; public static String nickname, ago, country ; // JSNI methods to get score data. public final native int getRank() /*-{ return this.rank; }-*/; public final native String getNickname() /*-{ return this.nickname; }-*/; public final native int getScore() /*-{ return this.score; }-*/; public final native String getAgo() /*-{ return this.ago; }-*/; // public final native String getCountry() /*-{ return this.country; }-*/; public final LeaderboardScore getLeaderboardScoreObject(){ return new LeaderboardScore(getRank(), getNickname(), getScore(), getAgo(), getCountry()) ; } } } public class LeaderboardScore { public int rank, score ; public String nickname, ago, country ; public LeaderboardScore(int rank, String nickname, int score, String ago, String country){ this.rank = rank ; this.nickname = nickname ; this.score = score ; this.ago = ago ; this.country = country ; } }

Then, i can inject that array of LeaderboardScore in the UI :

6. Custom loading bar

When you launch your game in the browser, you should see the default loading bar with a libgdx logo on top. To put the crazygames logo there, I simply went to myRootFolder/html/build/dist/html and erase the « logo.png » with the new crazygames logo.png. The loading bar size will automatic adapt to your image size.

7. Google analytics - Knowledge is power.

One of the big difference between the flash version and the HTML5 version of Boomerang Chang is the addition of a tutorial phase.

We wanted to be sure the players understand the game mechanic before they give up too early. So I added some Google Analytics (GA) events at strategic places of the tutorial phase.

With all those events in place, I was able to see that more than 80% of players completed the tutorial (which was far better than expected)

The events placed at the completion of each tutorial step can give you that kind of insight.

Technically, I also had to queue the events and dispatch them at interval because I guess there is minimum amount of time necessary between each call to the GA API. So I stored the events in a temporary array and then, each half-second I dequeued one event to send it to GA :

In java / JSNI :

// exemple code to send an event at the completion of a tutorial step private void onTutoStepComplete(){ trackEvent("TUTO_STEP_COMPLETE", "STEP_1_JUMPING (3/3)") ; } private native void trackEvent(String category, String action) /*-{ $wnd._gaq.push(['_trackEvent', category, action]); }-*/;

The GA javascript code on index.html :

var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-xxxxxxx-x']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')\[0\]; s.parentNode.insertBefore(ga, s); })();

For that to work, you need of course to setup a google analytics account and a new property for a website.

Fairly simple !

8. Going full screen - Great user experience

To make a big impact, your game would better go full screen. That really is an easy step, you just have to uncomment the pre-backed code that libgdx gives you by default in your HtmlLauncher.java .

9.Prevent special keys to affect the web page that hosts your game.

The problem was that pressing the arrow keys when playing the game would scroll the webpage that contains the game. The solution was thus to catch those keyboard events in javascript and stop them before could propagate to the web page.

To do that, i found some useful javascript code and added it the the index.html (located in : myRootFolder/html/build/dist ):

window.onkeypress = function(event) { // prevent the space key (page scroll down) if(event.keyCode == 32){ event.preventDefault(); return false; } }; window.onkeydown = function(event) { // prevent all navigation keys except the space key if(event.keyCode >= 33 && event.keyCode <= 40){ event.preventDefault(); return false; } };

9. Sounds

For the desktop browsers, everything was working perfectly out-of-the-box.

On mobile devices, it is another story…

I still have work and experiment to do as I have not been able to play smooth sounds on mobile devices.

I was able to play sounds, but only one at a time, and there was a delay making it look laggy… I guess there is something to do with the libgdx assets manager or something to configure in the soundmanager2.js .

Also, to avoid the sounds being blocked on mobile devices, I needed to bind the play of the first sound directly to a user interaction. What I did was catching the Javascript « tap » input event to call an initGwt function in the Java code that plays a white sound. That allowed access and avoid the sounds to be blocked :

In javascript, (in index.html) :

document.getElementById('embed-html').addEventListener('touchstart', handleTouchStart, false); var isFirstClick = true ; function handleTouchStart(evt) { if(isFirstClick){ isFirstClick = false ; window.initGwt(); } }

In java :

// in some init phase : public static native void doPrepareForMobileDevice()/*-{ $wnd.initGwt = @pp.manager.PPSound::doPrepareForMobileDevice(); }-*/; // in my sound manager public static void doPrepareForMobileDevice() { FX_WHITE_SOUND.play() ; }

As being said, this allows one sound at a time to be played, only on some mobile device, and in a laggy way… Not really what I was going for… So for the time being, there is no sound in the mobile version.

And voila, our journey stops here :)

Conclusion

We got our nice little Boomerang Chang HTML5 game working. The whole process took me a little 2 weeks and I’m really happy with result, the performance and the new opportunities offered by HTML5 I wasn’t aware of. Even if HTML5 is not the main focus of libgdx, I really think it was worth trying. And I’m now planning to port my others games to HTML5 !

I hope this article can help some of you guys and maybe encourage you to try out the libgdx HTML5 export. Because the magic of seeing your game playing in the web browser in real…