Dillo Hills: Optimizing Games for Playbook and Mobile

[This is a guest post by Justin Smith, who created Dillo Hills for the PlayBook, Android, and other mobile platforms. Justin has some great insights to offer on creating games for these platforms!]

 

PREFACE

Over the past few days, I’ve been working very closely with Adam Schroeder from FGL to playtest Dillo Hills on the Playbook. He’s been very helpful in the testing process, and he’s asked me if I’d be interested in writing about my experience developing the game: from the basic experience of working in AIR, down to some of the specific tricks I’ve used to optimize rendering for mobile.

PROJECT OVERVIEW AND STATS

Dillo Hills was my first experience programming for mobile devices. I worked with an artist (changko) and an audio guy (strike911), both of whom were also new to mobile development.

The game was originally developed for the Android platform, but the code for the Playbook version is almost completely identical, so all of the tips and tricks I talk about here should apply to both platforms!

The game, for anyone who is unfamiliar, is essentially a spinoff of Tiny Wings. Your main goal is to build up speed by rolling down hills, then use that momentum to ramp up into the sky and soar. We built on to the original concept by adding in obstacles, bonuses, and by tweaking the physics a bit to give something that feels a bit more fast-paced and visceral than the original game.

The game was released on Android near the end of March, was approved for the BlackBerry Playbook a couple days ago, and will be coming to Flash and iPhone in April (hopefully). The entire development of the game, from the moment I got the idea to the moment we released on the market, took exactly 3 weeks.

Reception has been mixed, but mostly positive (4/5 on Android Marketplace). The biggest source of negative reviews has been from users who are simply not able to run the game at an acceptable framerate, or experience problems installing the game. Out of all the users who are able to install and run the game smoothly, feedback has been reassuringly positive.

Sales have been pretty nice. Nothing outrageous yet, but certainly worth the time we invested, and still growing steadily. During the first 10 days after the game was released, we sold roughly 3000 premium versions of the game, and had about 25,000 downloads of the free version, which yielded just under 300,000 ad impressions using AdMob.

As for my own impressions of the game? I’m very excited and pleased with where the game is headed, but I’m also very eager to continue working on the game, improving performance, and adding some more gameplay features to keep people entertained longer. I’ve learned a lot of important lessons that will probably have an impact on how I develop games for mobile in the future.

 

OPTIMIZING TIPS AND TRICKS

1) GPU vs. CPU

The first hurdle you’ll face when developing a game for mobile is deciding whether to use GPU rendering or CPU rendering. Based on my experience, it seems like GPU rendering is faster when you are working primarily with bitmaps (or objects that have cacheAsBitmap=true) and placing them directly onto the stage / display list, and CPU rendering is typically faster when rendering vector shapes or using blitting as your primary means of rendering. Again, this is only based on the testing I’ve done, and your own results may vary. I recommend trying both until you find the one that works best for your game on the most devices.

For Android applications, you can choose either CPU or GPU rendering from the Publish Settings menu (in the Flash Pro IDE). For the Playbook, I had to manually add this line inside the <content> object (which is inside the <initialWindow> object) of my DilloHills-App.xml file:

<renderMode>gpu</renderMode>

However, when I enabled GPU rendering before compiling to a BAR file, the BAR would no longer render anything but a black screen when testing in the Virtual Machine. This appears to be a bug with the VM, as the game runs fine on the actual device. It’s also worth mentioning that Eric Heimburg from FGL was able to get his games to run inside the Virtual Machine with GPU rendering enabled, and it actually showed a pretty significant performance increase even within the VM, so be sure to try it out for yourself!

Dillo Hills was written and optimized for GPU rendering, so most of the techniques I mention here will be directed towards GPU rendering. A lot of these techniques are also great things that you should be doing in all of your applications (especially object pooling!), but are going to be nearly mandatory when developing for mobile.

 

2) Size does matter

And smaller is not always better! With GPU rendering, you want your bitmaps’ dimensions to be as close as possible to powers of 2 (i.e.:16×32, 128×128, 1024×256). In almost all of the tests I ran, I actually got significant performance boosts by increasing the size of my BitmapData objects to get to a power of 2 than using the smaller dimensions.

For instance, even though Dillo Hills’ characters’ dimensions are only in the ballpark of about 180×180, I was able to improve the game’s performance significantly by rendering them to 256×256 BitmapData objects.

I haven’t had a chance to test whether there is any sort of difference here depending on which platform you’re targeting, but my suspicion is that following this “rule of twos” is going to be helpful for any situation where you’re relying on GPU rendering, whether that GPU is in a phone, a tablet, or even a PC.

 

3) Scaling and rotation are expensive

It’s one thing when the renderer has to draw a bunch of bitmaps to the stage. It’s a totally different ballgame if it has to transform those bitmaps first!

Scaling, rotation, and alpha transformations are very slow to render, and should be avoided as much as possible. If you know ahead of time that something is only going to scale within a certain range, or is going to be rotated to a handful of specific angles, try pre-rendering an array of BitmapData objects at each of the desired angles/sizes so that the renderer doesn’t have to do the transformations every single frame while the game is playing!

 

4) Frame skipping

A common technique when building a game is to design all of the graphics as vector art (either in Flash or Illustrator), then prerender everything into BitmapData objects when the game starts. Then, as the game is going on, you render a different bitmap each frame, to give the effect of animation.

This technique works beautifully when building a Flash game for PC, but you will quickly run into two problems on mobile devices.

First problem: prerendering every single frame of an animation to a BMD object uses a ton of memory, and that’s something that a lot of phones simply cannot afford. Your computer probably has at least 4GB of RAM, but high-end phones and tablets are just now starting to reach 1GB, and most of that is going to be used by the operating system or other applications running in the background.

Second: rendering a new Bitmap each frame is a lot of work to process for a mobile device – especially if that new Bitmap has to be scaled or rotated.

The solution we discovered is actually quite simple – frame skipping! Essentially, we only prerender about one in every four frames of each character’s animations. This means 75% less memory consumption, and 75% less rendering stress.

This was actually one of the single biggest ways that we improved performance after releasing the game. When we implemented a 1:5 frame skipping ratio on Low graphics settings, framerate on many devices improved by as much as 12 FPS. This was especially noticeable on the Droid X, which only got about 8-10FPS at launch, but jumped to ~25FPS after frame skipping was patched in.

 

5) Object pooling

**This is something you should be doing already, even if you have no intentions of ever developing for mobile!**

One of the costliest things that your code can possibly do is create a new object: whether it’s a tiny little bullet, an enemy, or even a lowly utility variable like a String or a Boolean. A lot of programmers write their games in such a way that whenever something new is being added to the game, they just create a new object. When that object is no longer needed (monster is killed, bullet leaves the stage, etc.), they delete it, set it to null, and let the garbage collector take it away.

Creating and destroying new things like this is very slow to process. A much smarter and faster way to manage objects is recycling them.

In the world of programming, it’s usually called “object pooling,” but the philosophy is exactly the same as recycling: instead of destroying one thing and creating a new thing in its place, why not take something we no longer need, set it aside, and then reuse it later? Instead of destroying each bullet when it leaves the stage, we simple set bullet.visible=false and tell our engine to stop processing its movement, essentially putting it in a “recycle bin” mode. Then, when we need to make another bullet, we grab one of those invisible bullets in the “recycle bin”, put it in the new position, set bullet.visible back to true, and go from there!

(Bonus tip: when you’re done with an object and ready to put it in the “recycle bin,” don’t remove it from the stage: just set visible=false. Adding and removing objects from the stage is slow, but turning their visibility on and off is fast! When we made this adjustment to our score popup/particles on High settings, rendering speed when picking up crystals improved by about 4 FPS on our test device.)

This technique doesn’t have to stop at little things like bullets, either. The same principle can be applied to enemies, background scenery, tiles, particle effects, and even variables like BitmapData objects. In fact, in Dillo Hills, even the terrain and the sky in the background are just object pools of Bitmap objects that get recycled as you move from left to right! (If you could see beyond the left and right edges of the screen, you’d see where little “chunks” of the terrain disappear on one side and reappear on the right side.)

Shane McCartney has written a fantastic article on object pooling, including a sample class that I  highly recommend.

 

6) Manage your variables wisely

While rendering is usually going to be the bottleneck in a GPU application, you can always try to squeeze a little more performance out of your game by optimizing the rest of your code. This becomes significantly more important when using CPU rendering!

First, you should always make sure you’re using the right type of variable for each task. For instance, you should only use Number variables if you absolutely need to have decimal precision. If you know you’re only going to be storing integers, use an int. If you know those integers are never going to be negative, use a uint. If you know that everything inside an Array is going to be the same type of object (whether it’s a bunch of strings, booleans, or even complex objects/classes), use a Vector instead!

Second, you should always strive to remove instantiation from loops. In other words, you should avoid creating new variables as much as possible. If you know you’re going to be using the same variable over and over for simple tasks like counting a for loop, or calculating velocity, or checking the distance between two objects, consider declaring them as class variables instead of creating and destroying them every single frame!

 

7) Avoid textfields

The built-in textfield/TLF objects that you can create in AIR are simply too much to render at a reasonable speed. They’re fine and dandy if you’re designing a simple app that only needs to run at 8-15 FPS, but if you’re building a game, you should consider pre-rendering all of the characters you need to bitmaps, and then rendering them just like any other bitmap. This is a technique that has been used in video game development for years – Flash developers have been very spoiled by TextField objects, and it’s time to get weaned off of them!

 

8 ) Don’t feel like you’re obligated to develop for high-resolution display

A lot of modern smartphones have surprisingly high screen resolutions and surprisingly small screen sizes. In the world of computer monitors, 72DPI displays are the norm. Phones, on the other hand, are all about packing as many pixels into as small an area as technologically possible, and the result is ultra-high DPI devices that rival even modern print. In fact, the iPhone 4′s “retina” display claims a 326DPI display, which is roughly 9% higher pixel density than print, and 453% more dense than a normal computer screen.

Trying to render at such a high resolution puts a huge strain on the system. After all – when was the last time you wrote a Flash game that rendered out at 800×480 (Android), 960×640 (iPhone 4), or 1000×600 (Playbook)? Probably never. Most Flash games are designed to run at sizes closer to 640×480: sometimes even smaller. Smaller rendering sizes mean less work for the renderer, and less work for the renderer means a smoother, crisper framerate. When you combine the higher resolutions of mobile devices with their lower hardware specs, you’ll start to see why performance is a huge issue on mobile devices.

Ultimately, we decided that sometimes it’s okay to cheat. I mean sure, the target resolution for most Android devices is 800×480, but does our game really need to be displayed at print-level pixel density? I think not.

Our main character and our UI are rendered at 1:1 pixel ratio, because they are important visual focus points, and we wanted them to appear crisp and sharp. Besides those things, every other single item in the game is rendered to a BitmapData object at a reduced resolution, then scaled back up at runtime. This means less memory used, less strain on the renderer (as long as you’re only scaling once, not every single frame), and only a small impact on visual appearance. The nice thing about this technique is that you can take it to more extreme levels to compensate for weaker machines. In Dillo Hills, the lowest graphics settings will actually cause most bitmaps to be rendered out at ¼ their full resolution!

 

9) Include graphics settings

One of the things I wasn’t really thinking about when I first started programming Dillo Hills was an options menu with graphics settings. What I quickly discovered was that in the world of Android, every user’s phone is going to be completely different, and there is no shame in asking them to choose quality settings appropriate for their machine!

Just be prepared to deal with your users on a psychological level: a lot of gamers are going to hope/assume that their phone can handle the graphics at the highest settings, and then get angry when it can’t. I’m actually really considering using the labels “Normal, High, and Very High” for my graphics settings, instead of “Low, Medium, and High,” just to make sure people got the right idea.

 

10) Save gamestates instead of pausing when minimized

If you’ve read through the FGL tutorial, you’re aware that one of the things you’re going to need to do for your game is deal with the player minimizing your game: maybe to go check a text, maybe when they receive a phone call, or maybe just because their project manager walked in the room and wants to see how the presentation is coming along… not that I would know anything about that!

Most programmers will instinctively just pause their game and mute all sounds while it’s in the background. This is fine and dandy, except for one small problem: if the game is still running, it’s still rendering, and if it’s still rendering, it’s eating up the phone’s battery!

What I recommend (and what I intend to do with Dillo Hills in an upcoming patch) is to completely shut the game down while minimized, but to save the current gamestate to a SharedObject before closing. Then, when the user reopens the application, all of their existing game information will be loaded back in, and they will be given the option to pick up exactly where they left off.

 

MY OVERALL IMPRESSIONS WORKING WITH AIR FOR MOBILE

1) Setting up your IDE and programming for mobile is not scary.

Overall, my experience developing for Android has been quite good. The process of setting up the IDE had a few gaps that weren’t documented as well as I would have liked, but it still only took an hour or two to figure it all out. Once the IDE was set up, working with AIR was a breeze. Anyone who is familiar with AS3 will have no trouble making the jump – in fact, unless you’re building a game that utilizes mobile-specific capabilities (accelerometer / GPS / camera), you really aren’t going to have to do anything new or different other than handle menu button presses and activate/deactivate events.

Publishing for Android was just as easy. Free applications can be distributed with no prerequisite other than signing up for a developer account and uploading your APK file. Paid accounts require only a $25 license fee to upgrade your developer account to a vendor account.

Porting to the Playbook was also a breeze. All things considered, it only took me about 4 hours to convert the game from Android to Playbook, and almost all of that time was spent downloading/installing the Playbook API and Virtual Machine, not fussing around with code. (Note to anyone who is following the FGL tutorial: make sure you use version 0.9.4 or later of the Playbook API and the Virtual Machine software, not 0.9.3. Using 0.9.3 resulted in some very unusual crashes when I tried to play audio.)

 

2) The Android market has a huge degree of fragmentation.

As I mentioned earlier, almost all of the negative feedback we’ve received so far is related to poor performance on a handful of specific phones (in particular: the Droid X, the Galaxy S, and pretty much anything over 9 months old). When you’re developing in Flash, you can really get away with a lot of dumb stuff without noticing any major performance problems. When you’re developing for mobile, though, you’re going to face performance problems even if your game is optimized beautifully. There are simply too many different phones spanning a massive range of hardware capabilities to get a reliable experience from phone to phone.

We also encountered a lot of unexpected results once the game hit the market. For instance, the Samsung Captivate has consistently outperformed the Droid X, even though it is almost 5 months older. Equally surprising: we have had a lot of people reporting worse performance using rooted/overclocked phones than users with the equivalent base models!

Just out of curiosity, I checked out the reviews for a handful of other popular Android games, and found that even some of the biggest developers are getting the exact same frustrations from fragmentation:

Fruit Ninja (Halfbrick Studios): Works perfectly on most phones, even as far back as the Motorola Droid, but some newer phones experience input lag. The Droid X seems to be an absolute wildcard – some users report flawless gameplay, while others report severe lag and crashing.

Doodle Jump (GameHouse): Works perfectly on most phones, but a handful of phones lag or crash, including the Wildfire and the Droid.

PewPew 2 (Jean-François Geyelin): Works perfectly on most phones, but crashes on the Droid X and the Nexus S.

Angry Birds Rio (Rovio Mobile): Works perfectly on most phones, but lags and crashes on some models, including the Moment, the Xperia, the Slide, and the Legend.

The moral of the story is that with Android, you just can’t ever really know for sure how your game is going to run until it’s out in the wild. Obviously, it’s important to optimize as much as you can to increase the number of phones that run your game well, but it’s going to be nearly impossible to get it working for everyone.

 

3) The Android platform is prone to piracy.

So far, roughly 20% of all people playing the premium version of Dillo Hills are playing a pirated version of the game – in spite of the fact that a free version of the game is available in the market. I haven’t seen any hard data yet, but other people I’ve talked to have reported piracy rates as high as 80%.

There are many reasons why the piracy rate on the Android platform is so high, but the simplest explanation is that Android is just a very open platform. It’s easy to access and modify files on your phone, it’s easy to install applications directly from an APK file, and it’s easy to extract the APK file from an installed application. (In fact, it can be done using file management software that is available directly from the Android market.)

This is something that Google is actively working on, and I was happy to see that they are replacing their existing copy-protection system (which was cracked within mere days of its announcement) to a licensing system. However, even this has drawbacks. First, the licensing system seems to only be accessible to applications developed in Java or Objective-C, not AIR (if anyone knows otherwise, please speak up). Second, DRM systems almost always end up causing more frustration than they’re worth. How many times have you pirated a song/program that you already own because the DRM stopped working, or only worked when you were online?

It’s a tough problem, and it’s one that the software industry has been dealing with for years: it’s just now getting to the point where Flash/AIR developers will really get their first taste of it. Up until now, all we’ve had to worry about was decompiling and illegal distribution (which usually ended up helping us more than it hurt, anyway). This problem is harder to fight, and hurts us much more.

Will we ever see a foolproof solution to piracy? Almost certainly not. For the time being, developers moving into the Android market just need to be prepared to deal with the fact that a lot of people are going to be stealing the products of their hard work.

 

SUMMARY AND CLOSING THOUGHTS

Hopefully, I’ve been able to offer something useful or insightful. I’m actually really looking forward to hearing what tricks other developers have come up with to optimize their games for mobile, because I’m sure there are all kinds of crazy things I haven’t even dreamed of!

 

8 Responses to “Dillo Hills: Optimizing Games for Playbook and Mobile”

  1. Amazing post, thanks a lot! If this post would have been here three days ago it would have saved us three days of learning A LOT about performance issues. Now it proofs what we found out. :D I think everyone should really read this one.

  2. Brilliant information, I’ll definitely be referring back to this a lot in the future!

  3. Actually, according to my own tests, an int is faster than an uint, so uint should be used only when you need really large numbers.

  4. Other very good reason for android to have so high piracy is simply you can’t buy apps in like 90% of the world. In my country (chile) you can’t buy anything from the market, only download free apps. So the only way for us to get a paid app is pirating. Good google, you are doing it great to devs and users!

    BTW thanks for the great post! this will go definelly to my bookmarks!

    -Israel

  5. I don’t think the height of a bitmap matters, just the width, so you only need the width of your graphics to be a power of two. This is because of the way the screen data is stored in memory, so if for example your image is 17 pixels wide, 32 pixels will be rendered, but the gpu will still only loop though the correct number of lines (height) as it needs to.

  6. Awesome explanation! This is the best performance analysis for smartphone programming I have ever seen!

  7. Now that Stage3D is finally supported on mobile, I wanted to go through and make some corrections, updates, and new points.

    —————————-

    RE: 1) GPU vs. CPU

    There is now a 3rd option on the table for Android and iOS: direct

    This option will allow you to utilize Stage3D, either by directly rendering everything yourself using quads (if you’re more comfortable with a blitting style of rendering), or using an engine like Starling (which is very similar to displaylist rendering – the default rendering mode in Flash Pro).

    It’s important to note that you cannot choose “Direct” from the publishing options in CS5/CS5.5: you’ll need to open up your descriptor XML file, change it manually (scroll down if you don’t see it – Flash Pro likes to add a bunch of whitespace to your descriptor file for no reason), and then open up Properties and set it to Read Only so that Flash doesn’t overwrite it while publishing.

    It’s also important to note that in CS5 / CS5.5, it isn’t possible to use the Direct rendering mode while debugging, so you either need to use remote debugging on your phone, or publish to an HTML file (F12) and use a 3rd party program like Vizzy to watch for traces/errors. If you’re using Starling, this won’t be a problem for you: it automatically falls back to blitting if direct mode isn’t available.

    —————————-

    RE: 2) Size does matter

    This is still true, but you can avoid this problem entirely by stuffing all of your graphics into a texture atlas. Which brings me to a new point!

    —————————-

    Use Texture Atlases!

    A texture atlas is nothing more than a bunch of spritesheets crammed together into one big texture.

    With Stage3D (and any other GPU-accelerated rendering system), there is a penalty every time you switch from one texture to another, because the new texture has to be loaded onto the GPU before it can be used. (This is unlike blitting, where you can change source textures as often as you want for no penalty.)

    What this means is that the fewer times you jump back and forth between textures each frame, the better. Instead of rendering one enemy from his spritesheet, then another enemy from a different spritesheet, then another enemy from yet another spritesheet, it’s much faster to stuff all three enemies’ spritesheets into a texture atlas, then render all three enemies back to back from that atlas.

    You can use software like TexturePacker to make spritesheets and texture atlases. There are some wonderful tutorials by Lee Brimelow showing exactly how to do this. It’s actually sort of a fun mix between creativity and organization: you’re trying to plan out which things will be rendered together most often, and then try to pack as many of those things together as you can. Oh, but this next point is relevant:

    —————————-

    Try to avoid textures larger than 1024×1024 unless you’re going to be saving a TON of texture swaps.

    Even though AIR can technically support larger textures, many phones have a difficult time working with textures larger than 1024×1024, and will apply a speed penalty when using them. In the tests I ran (on an HTC Inspire 4G), I had significantly better results using 8x 1024×1024 atlases versus using 2 2048×2048 atlases, even though the 1024×1024 method required more texture swapping.

    —————————-

    RE: 3) Scaling and rotation are expensive

    Not any more! In Stage3D/Starling, scaling and rotation are dirt cheap! You can even use color transforms with pretty low overhead, although you should still render responsibly. :D

    —————————-

    Use BlendMode.NONE for background images, tiles, and anything else that doesn’t have an alpha channel.

    The default blend mode in Starling will account for an alpha channel. You can change the blend mode to BlendMode.NONE for background images and anything else that doesn’t need an alpha channel for a significant performance boost.

    —————————-

    Set .touchable=false for anything that doesn’t need to be clicked/touched.

    Again, the default setting for movieclips is to listen for mouse/finger events. You should turn it off for almost everything.

    —————————-

    RE: 5) Object pooling

    You still need to do this! Object pooling is not optional for mobile: it is required if you want good performance. In a perfect world, you shouldn’t be using the “new” keyword at all in your main game loops.

    However, a quick note about Starling MovieClip objects! By default, you can only set the texture by passing in a parameter with the “new” constructor, which means that by default, you can only object pool MovieClips that share the same texture atlas. If your game only needs one texture atlas, that’s great, but many games will require more. To get around this problem, you can add the following function to starling.display.MovieClip:

    public function changeTextures(textures:Vector., fps:Number=30):void{
    if (textures.length > 0) {
    mDefaultFrameDuration = 1.0 / fps;
    mLoop = true;
    mPlaying = true;
    mTotalTime = 0.0;
    mCurrentTime = 0.0;
    mCurrentFrame = 0;
    mTextures.length = 0; // = new [];
    mSounds.length = 0; // = new [];
    mDurations.length = 0; // = new [];

    for each (var texture:Texture in textures)
    addFrame(texture);
    }
    else
    {
    throw new ArgumentError(“Empty texture array”);
    }
    }

  8. naturally like your web site but you need to test the spelling on several of your posts. A number of them are rife with spelling problems and I to find it very troublesome to inform the truth on the other hand I’ll definitely come again again.

RSS feed for comments on this post. - TrackBack URL

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>