Last summer, I got to intern with Appirits, a web services company located in Tokyo, Japan. Aside from doing typical web stuff, they also have a department dedicated to making online games, known as the Appirits Game Project.
Most of their games are browser-only, and nearly all are Japanese-exclusive. However, one of them, Shikihime no Niwa, is available on iOS and even has an “English” version (inasmuch as “Summon Strong Hime!” is English). My job for the summer was to port over the 百鬼夜行 (“Pandemonium”) game mode from the browser version to the iOS version.
Since Japanese companies do not generally have summer internships as we do here, it was a very different experience from what I was expecting. While most companies here place a heavy emphasis on mentoring and creating a carefully structured intern project, Appirits took a very much hands-off approach—an approach that, I feel, gave me more freedom and control over how the project was completed.
I was given access to the client and server code for both the browser and iOS versions of the game. Getting things set up and running was a bit of a struggle at first; the company wiki with the deployment instructions was all in Japanese, and some of the details were outdated. But about a week in, I managed to get copies of the game running on my local machine.
While the server side was implemented with Ruby on Rails, which I am somewhat familiar with, the client side was implemented with Adobe AIR, a system that allows developers to deploy applications built with Adobe Flash across a variety of platforms. As you may know, Flash isn’t exactly the hottest technology right now, but, as with many semi-obsolete technologies, it’s still quite popular in Japan. However, the cross-platform nature of AIR should make porting things over a cinch, right?
Ha. Not so. Even after I managed to copy all the relevant code between the versions and got the app running in the AIR Simulator, I was far from done. It turns out that there are quite a few restrictions when AIR apps are run on mobile devices.
However, this functionality is extremely restricted on the iOS AIR runtime. SWFs that contain any sort of ActionScript code at all cannot be dynamically loaded, and even then performance is very shaky when more than a few are onscreen at a time. In addition, there’s a longstanding bug affecting AIR where certain SWFs cannot be reliably unloaded, resulting in memory leaks over time.
My solution was to strip ActionScript code from the existing SWFs, pre-rendering and caching the now “pure asset” SWF’s contents in the background, then manually animating the cached images by swapping the image to be displayed per frame. This process, though simple in theory, was again complicated by runtime limitations.
Stripping out ActionScript code (technically ActionScript Bytecode or “ABC”) was surprisingly difficult. There’s no built-in utility for this, and since the SWF file is a proprietary binary format, doing it by hand would be impractical. I ended up using the open source as3swf library to write my own tool to manually strip out the appropriate tags. Annoyingly, many of the SWFs contained no actual code at all, but empty tags would cause the runtime to complain anyway.
The pre-rendering process was also fraught with limitations. The mobile Flash runtime is fundamentally single-threaded, and there’s no real way to do something “in the background.” To get around this, I implemented a pseudo-threading system, where each render operation would have a fixed amount of time per frame to do its job before waiting until the next frame to continue. While this was imperfect in many ways, it worked well enough to avoid obvious framedrops.
Finally, I had to implement the caching mechanism. Initially, I simply held onto the cached resources for as long as the game mode was onscreen, a strategy that actually worked on all devices except the iPhone 4s, which only has 512MB of RAM. On the iPhone 4s, as more resources were cached, the game would get slower and slower until the OS killed the app entirely. That was unacceptable, so I decided to investigate the cause of this behavior.
On iOS, the system sends apps a UIApplicationDidReceiveMemoryWarningNotification (hooray RidiculouslyLongCocoaNamingConventions!) whenever memory gets low. The AIR runtime intercepts that call and activates the garbage collector. However, the garbage collector is unable to reclaim the memory taken by the cached resources, since those resources might still be in use. Ideally, the app should be notified when the memory warning is sent, and the cache should evacuate any contents not immediately needed. This required me to write an AIR Native Extension to bridge native and ActionScript code, since the runtime does not expose the memory warning to the ActionScript side by default.
After lots of debugging, optimizing and testing, the game was finally running acceptably! However, the UI was still very much designed for a mouse and keyboard, with tiny text and scrollbars everywhere. Since I still had quite a lot of time left in my internship, I ended up redesigning and implementing a new finger friendly UI, using the Apache Flex framework, which was a whole other can of worms.
All in all, I was pretty satisfied with what I managed to get done. While I may not have had the guidance or mentoring I had anticipated, I still ended up learning quite a lot, perhaps even more than I would have elsewhere. I got to act as not only the programmer, but also the tester and the designer. Aside from the general goal set in the beginning, I wasn’t really given concrete steps to follow, so I got to decide for myself what things were important, how to implement things and when things should be done—something I don’t think I could have experienced in a more traditional internship environment.
Interning in Japan was definitely a once-in-a-lifetime opportunity, and one I wouldn’t have traded for anything else. If you’re interested in potentially studying or interning abroad, look into the Bing Overseas Studies Program — it truly is a life-changing experience and one I cannot recommend enough!