I’ve been pretty quiet on the blogging front this year, the reason being I have been spending the majority of my time developing an HTML5 game that I have released on web, iOS and Android.

The game itself is pretty simple, you tap-to-rotate tiles on the screen which allows a little truck to make its way around collecting different pickups for points and game-altering effects. I wrote the prototype for the game in Unity in a couple of days at the beginning of the year, and liked it enough that I decided to attempt a public release. My initial thoughts were that it needed to be as accessible as possible, so I’d target a launch on Web, iOS and Android.

Prototype in Unity3d Prototype in Unity3d

Building games in Unity can be pretty quick once you understand how it works, but exporting to Android and iOS is painfully slow and their new WebGL player is not very lean with considerably larger download times on web. I decided to embrace Javascript (actually TypeScript), found the excellent open source Phaser framework, and rewrote the game as an HTML5 application.

There are existing packaging systems for distributing HTML5 across platforms like Apache Cordova, Intel XDK and Cocoon, these all have their pros, cons and weight. I determined that I would have the most control over how the game was presented cross-platform if I wrote the native hosting apps for mobile myself. The caveat meaning that I would also need to write Swift and Java in addition to writing the game in HTML5.

Typescript, Swift and Phaser were entirely new me, and I wrote some Java in 2001, but I had not written (or owned for that matter) anything on Android and this meant the learning curve would be steep. The Phaser re-write came around pretty quickly (although not as quick as Unity) but the real challenge was writing the iOS and Android shell apps to host the game on mobile whilst attempting to achieve the same performance as running on a PC browser.

Performance

Spending time on game dev subreddits and html5 development forums somehow gave me the idea that HTML5 was a mature technology that has good support for, and works well across, multiple platforms. This view is pretty close to plain wrong, specifically for mobile. It is extremely impressive what you can achieve game-wise in HTML5 on a decent machine where resource availability is almost taken for granted, but that is just not reflected on mobile and particularly on Android. It’s not impossible to get good performance out of HTML5 games on mobile, its just difficult if you don’t know how. The adage of “write once, run anywhere” in this case is more of a “write once, hack per platform until it works”.

Through a bit of trial and error and headbanging I determined the following key items that contribute the most to better HTML5 game performance on mobile;

  1. Don’t use web audio, instead pass all sound processing to the host app and process/play it natively. This means SoundPool for SFX and MediaPlayer for music on Android, and AVAudioPlayer in iOS.
  2. Don’t use the Android native browser. Crosswalk, although adding about 20MB to the APK download size, is mandatory on Android and contains a version of Chrome that has far better performance than the standard WebView. You’ll need to add Crosswalk ProGuard rules, and if using Google Analytics, you’ll need to bypass the checkProtocolTask and switch cookies for localStorage.
  3. WKWebView has far better performance than UIWebView on iOS, however you’ll need a webserver like GCDWebServer as WKWebView does not support local XHR requests.

Mobile WebView Functionality

The WebViews (basically embedded browser viewports) don’t have all the functionality you’d expect from a desktop browser. Here are some items that I needed to resolve specific to mobile platforms;

  • Background switching
    • I found that iOS would crash during background switching due to WebGL calls being illegally made in the background. I needed to add native observers to detect background switching and turn off WebGL rendering while in the background.
    • Because audio is being processed natively, it needed to be paused manually whenever the app went into the background.
  • Alerts
    • Calling alert() isn’t going to result in a prompt unless you implement it yourself via runJavaScriptAlertPanelWithMessage in iOS and AlertDialog in Android.
  • _blank target hyperlinks
    • These won’t open in a new window on either XWalkView or WKWebiew. For both, url loading needs to be intercepted natively and (where neccessary) the native browser opened programatically.

Advertising

The game is free on Web, and free but ad-supported on mobile. I don’t really have much of an idea about advertising networks so I just went with the two largest - Facebook Audience Network and Google Admob. Facebook’s not available in China so Admob is the fallback. Most, if not all, tutorials and developer guides include messing around with layouts to implement these so it took a little while for me to figure out how to do it programatically on both platforms. I found it much easier to implement both ad networks on iOS than I did with Android, with the Android side taking about twice the time, even though I had to figure out Objective-C to Swift bridging for iOS.

Finished Game Finished Game

In-App Purchases

I have one IAP, for removing advertising, however implementing this in the Android and iOS was non-trivial, and indeed for Android I did it twice. Google Play has a handy guide for implementing an IInAppBillingService directly which I implemented first, however elsewhere on their developer’s site are instructions and a library for implementing IAPHelper which is a higher level and far easier to implement. I found that after double and triple checking configuration and implementation during development some calls would often fail. For example, I could successfully retrieve a list of IAP items that were available, however attempting a purchase would error due the item not being available(!?). Stackoverflow seems littered with questions that have ambiguous answers specific to this when unfortunately it seems waiting is the only thing that resolves such symptoms. Multiple times when receiving failed IAP responses the fix was simply to do nothing, and testing the same code base 12 hours later would then result in success.

Implementing an IAPHelper on Android is good practice before attempting the same on iOS, as I found the latter to be much harder, possibly because of the instability of (at the time) of Apple’s sandbox servers. This tutorial was extremely helpful. I had originally had a menu button - Remove 3rd Party Ads - that upon tapping would check to restore the previously purchased item prior to attempting a purchase. Unfortunately on the day the app was reviewed by Apple, their sandbox was broken for restore, so the button would just silently fail. The app was originally rejected due to the silent fail, but also because no restore purchases was available (it was - it’s just their test sandbox was down). Communication with Apple takes days, so rather than discuss the details with the review team, I just added a separate button for Restore Purchases and submitted the app again.

App Stores

With Apple’s review process I was seeing on average a six day turnaround for App submission and a 1-3 day turnaround for (an external) Beta App submission. As mentioned above the first submission was rejected. The second was accepted, but I found a sound issue (I was still using web audio on iOS) that day so I fixed it and submitted again. This meant the original release ended taking just over 18 days due to review queue. After uploading to the App Store the build is marked as Processing for about 15 minutes before you can make it available to Test Flight or submit it for review. Sometimes however it seems the queue gets blocked up, or Apple upgrades infrastructure (silently) and so sometimes this can take much longer. My longest was about 18 hours, and whilst again there’s a lot of ambiguous answers on Stackoverflow describing how this can be solved, it seems waiting is the answer. Apple’s official remark regarding this type of thing is to try uploading again if your build hasn’t become available in 24 hours.

Google Play is a lot quicker, with alpha and beta builds becoming available for download within about an hour, and a production build available for download after a few hours. APK splitting is a bit strange as it’s off by default meaning (larger) universal uploads are expected. Switching to advanced mode however allows you to upload seperate ARM an x86 builds, halving the download size.

You can play it now

Play it on the web, or google play:

Also on these sites: