OS Native UI in Wind-up Knight


A while ago I posted the following tweet to my twitter account:

Fun fact: #windupknight on Android includes 7575 lines of Java. iOS version has 7005 lines of Objective-C. Both have 12000 lines of C#.

This generated a lot of responses, mostly asking about what the Java and Objective-C code is for.  Today I’ll explain how I structured the OS-native code for Wind-up Knight, and what I learned in the process.

We absolutely love Unity to death, but one thing it’s terrible at is 2D UI.  Specifically, 2D UI that is based on atlas textures and that can automatically resize itself to different screen sizes and aspect ratios is a gaping hole in Unity’s otherwise formidable mobile game engine feature set.  All the 2D stuff we do in Wind-up Knight is code I wrote from scratch (or borrowed from the net); there are basically no tools for 2D-on-mobile and so you have to make your own.  Font rendering is particularly problematic.  Unity takes the old-school game engine approach of rendering all fonts with font textures, so if you want to support arbitrary UTF8 text, you’re in for a hard time (or some ugly fonts).

Early in the project I decided to rely on native OS APIs to do 2D UI rendering in order to get around this weakness in Unity.  It’s pretty easy to knock up some simple views and render them on top of Unity in both iOS and Android; in fact, both systems use view hierarchies that are so similar that I was able to copy and paste some code between platforms (and languages) with only minor syntactical changes.  My plan was to draw anything requiring text with the native OS API, and to draw everything else with Unity.  This would also make certain complex-but-common UI controls (like scrolling lists) easier to implement, as both operating systems provide them right out of the box.  We knew that we wanted to localize the game into Japanese (and maybe Korean and Chinese as well), so having a robust way to render UTF8 text was key.

To get native code support to work you need to follow a few steps.  On Android, I created a project outside of the Unity folder and then wrote a shell script that would package the generated .class files up as a jar and move it, along with the other project resources, into Unity’s Plugins/Android folder.  The workflow was to edit Java outside of Unity, compile it, build the Jar, and then rebuild the binary from Unity.

On iOS, the process is reversed: Unity outputs an Xcode project, so you put your native code files in the Plugins folder (or elsewhere; there’s some trial and error, and maybe a few shell scripts involved) and execute a Unity build first.  Then you can work on the generated project in Xcode to add Objective-C code, and finally compile the final binary from Xcode like any other project.

In theory, using native code for UI drawing this is a great idea.  Unity can bind itself to native code (via the [DllImport(“__Internal”)] declaration on iOS and via JNI on Android) and call down into it pretty easily.  On the native side you have tools and debuggers made for designing interfaces, and you can leverage all that stuff this way.  Plus both systems have a solution for different size screens and things like that.

In practice, it turned out to be a poor decision.  First, we ended up having quite a lot of non-trivial UI (even though Wind-up Knight is a pretty simple game, interface-wise), which required me to write those 7000 lines of native code twice in two different languages (and if we ever want to port the game elsewhere, I’ll have to write it again).

Second, the tools provided by Apple and Google are not artist-friendly tools.  They both require intimate knowledge of how the UI subsystems that run the OS work under the hood.  Therefore, the only person who can author UI with this setup is the engineer (that’s moi).  Unfortunately, engineers are rarely artists; though I worked from mockups generated by people who understand art and did my best to replicate them, the UI in Wind-up Knight is missing much of the polish that we put into the rest of the game.  Our artists, Mike and Casey, didn’t have access to the UI authoring environment and couldn’t get in there to work their magic.

Third, as the game got larger our Unity rebuilds got slower and slower.  This is especially problematic for Android because we need to do a Unity rebuild to test every change.  With 52 levels it takes Unity a few minutes to build and package our game as an apk, so even a one-line Java change requires a lot of waiting to verify.  It would be nice if Unity had an incremental build system for this kind of stuff.

Fourth, and perhaps most importantly, maintaining a few thousand lines of code in two different languages is costly and error-prone.  Porting Wind-up Knight from Android to iOS was trivial at first, but as our UI grew more complex the amount of work it required to mirror in Objective-C became significant.  In the end, between the two platforms, I wrote more UI code than game code (!!), and though that code was not complex, it was quite time consuming.  And the result was a serviceable but far from fantastic UI.

One thing about this plan that did go well was our plans for localization.  Thanks to OS native text rendering, implementing Japanese text was pretty trivial.  There are still some issues (word wrapping rules for Japanese suck on both platforms), but for the most part it worked exactly the way I had planned it.  Japan is our #2 market worldwide, and at this moment it’s #1 for iOS; I suspect that success has a lot to do with our Japanese localization.

Future Robot Invader games will definitely leverage platform APIs, but probably not for UI.  There are other things we do in Java and Objective-C, such as in-app billing integration, GameCenter calls, and Android Notifications.  Those kinds of things are quick and easy to do in the OS-native programming environment, and really don’t belong in Unity code anyway.  But for the next project, my plan is to spend some significant time building a proper UI system within Unity (or perhaps buying one of the available 3rd party libraries).



This entry was posted in Android, game engineering, iOS, mobile games, wind-up knight. Bookmark the permalink.

11 Responses to OS Native UI in Wind-up Knight

  1. gman says:

    It’s always surprising to most people how much work UI is. By UI I mean everything that happen when you’re not actually in the main game. The title, the options, the load/save stuff. etc. I’m not surprised that it ended up being more code than the actual game. I suspect that’s common for many games. Most people, including myself think “yea, I spent 6 months writing this game, now I’ll just spend a week slapping on some UI and ship it” only to find that there’s a huge amount of work left.

    PS: I suggest you switch to disqus for your comments. It provides lots of features (email notification, avatars, threading, phone app support, …) that aren’t in WordPress. And maybe it’s just my luck but no matter what spam solution I tried for wordpress I still got too much spam. Disqus seems to handle that better probably because it makes spam somewhat crowd sourced.

    The good news is it will likely only take a few minutes to switch. Install the disqus plugin for wordpress and you can import all your current comments.

    • Chris says:

      I guess I wasn’t surprised by the amount of work (and, if we count just one platform, the UI code is about 60% the size of the game code), I was surprised at how many problems the native platform UI systems have. I was dumbfounded that, for example, I can’t round corners on views without writing code. On both platforms, I can’t change the font of text without writing code. I expected the tools to go a whole lot further than they do–I was writing a lot of code for things that should most certainly be data.

      In any event, it worked in that UTF8 text rendering was not an issue at all. But the pain involved with porting all that code from platform to platform was so significant that I won’t do it this way again. Next time I write it all in Unity, though that will absolutely be more work up front.

      Thanks for the tip about disqus, I’ll look!

  2. Alex says:

    This was certainly a tough call to make. It’s hard to know how the alternative would have stacked up; writing a vector based font renderer in Unity would have taken longer, at least initially, and either way your code will be chucked when the Unity team get their act together.

    For other platform specific functionality (eg In App Purchasing), did you consider any off the shelf plugins? Prime31 have a wide variety available, though I cannot vouch for any.

    Finally, how do you deal with backing up people’s purchases? On iOS there’s iCloud, but If I wipe my Android phone is there any way I can get back the notes I’ve purchased?

    • Chris says:

      We didn’t use any off-the-shelf plugins–we just wrote them ourselves. IAP, especially on iOS, is trivial to implement, so we didn’t bother with third party code. With respect to backups, we have an iCloud-supporting build in the works, but no solution for Android yet. BackupManager might be the way to go, but it doesn’t solve synchronization across devices, which is what people really want. Still thinking about it.

  3. Alex says:

    Does the IAP situation give you a support headache?

    With up front app purchases, Apple or Google link them to a user’s account and handle (re)downloads. My concern with IAP is that much more responsibility for support is lumped on the developer. In some cases it even appears impossible to programatically retrieve a user’s transaction history (eg for consumable purchases).

    Do you have a manual process to reimburse people’s ‘lost’ notes, or is the policy ‘once they’re gone, they’re gone’?

    • Chris says:

      You are correct that consumable purchases cannot be confirmed later. On Android, this is a huge headache. We do have a system to reimburse lost purchases. On iOS, we have never had a complaint.

  4. Hao says:

    The book 1 level 9 (the first level requires you to use a sheild) has a graphic issue when something drops that u need to use sheild to block it.

    It happens in my HTC sensation. And my HTC system is up to date.

  5. Creelozegmems says:

    Hello! Just want to say thank you for this interesting article! =) Peace, Joy.

  6. Peter says:

    Very interesting post, thanks! I’m someone who been doing native development on both platforms and I’m getting sick of it, so I was looking at Unity.

    Can I ask why you didn’t consider any of the popular Unity 2d plugins like 2dtoolkit, ex2d, ezGUI, etc?

    • Chris says:

      We considered them, but they don’t solve the problems we were trying to solve: UTF8 text and resizable, variable density screen support. We did use SpriteManager as a basis for the 2D Unity-drawn elements in the game, though.