I just finished porting my game Train Conductor 2: USA from iOS to Android. It’s a plunge every good iOS developer should take… to transition from the well organised air-conditioned mall of the App Store to the often bewildering bazaar of the Android Marketplace. When taking this plunge, it’s very important to show Android some love. Don’t just port your game. You need to take steps to ensure that you are hitting a high quality benchmark and you need to be careful that you don’t think of Android as the “other iPhone”.
You see Apple are really quite good to us. Sure, sometimes they reject our games or our updates telling us to fix up some issue, but really that’s in our own best interests. Because Apple acts as a gatekeeper, ensuring a minimum level of quality for all apps. This engenders a certain level of trust from consumers – they aren’t so scared of parting with their $0.99 because they know they that the game they are buying won’t crash (too often) and if the game is featured by Apple, consumers know they are going to get something special.
Google do not act as a gatekeeper for the Android Marketplace. Anyone with $25 can signup and release more or less what they want. Many apps can be very buggy – especially on phones with unusual features – and so it’s very hard for a consumer to trust a game before they buy it.
Any iOS developer entering the world of Android must take it upon themselves to be their own gatekeeper and to make themselves aware of what constitutes best practices for an Android application. Just as iOS users want and deserve lists that move and bounce nicely when you scroll them, Android users want and deserve some key conventions to be observed.
Games must properly support the buttons. On iOS there is one button, and we usually don’t need to think about it too much. On Android there is back, home, search, and usually an options button too. These are commonly refererred to as the “hard keys”.
Games must play nicely with other applications. You know how in your iOS game, you detect when the user makes a phone call and you politely pause the game and wait until they come back? You need to do this and more for Android. Android has more complex multitasking than iOS, and so you need to ensure you game is a good Android citizen that will get along with all the other applications, activities, tasks, and notifications that will often demand your players attention.
First, let’s discuss how the hard keys are supposed to work.
The Hard Keys
Back. Don’t be confused by your web browser, on Android the back button is more like popping something off the stack. Whenever your player sees a screen, that screen goes onto the stack. When the user is done with that screen you then remove that screen from the stack.
An example:
Step 1. The user views the main menu (push main menu onto the stack)
Step 2. The user starts a level (push the level onto the stack – pushing back here should return the user to the main menu)
Step 3. The user finishes the level and returns to the main menu (pop the level off the stack – pushing back here should return the user to the Android home screen)
Home. There’s no place like home. The home button should always – always – return the user to the Android home screen. Don’t fight this. Let it happen.
Options. A fairly straight forward button really. If there are any settings the user can tweak, allow the user to tweak them after they press the options button.
Search. Also fairly straightforward, if the player is viewing something that can be search, they should be able to search by pressing this button. If you don’t have anything that can be searched, then this button should do nothing. Some dialogs allow themselves to be cancelled by this button, so ensure that you don’t allow search to behave like the back button.
In your main Activity, you will want to override the following methods:
1 2 3 4 5 | @Override public boolean onKeyDown(int keyCode, KeyEvent event) { //Generally you don’t want to handle a hard key’s onKeyDown. //Common practice is to perform the appropriate action on the key up, not the key down } |
1 2 3 4 | @Override public boolean onKeyUp(int keyCode, KeyEvent event) { //Here is where you usually want to handle the hard key. } |
You may also want to handle:
1 2 3 4 | @Override public void onBackPressed() { //... } |
Note that this is just a convenient way of simply overriding the back button. onKeyDown and onKeyUp would be my preferred way of managing the key press as those methods provide a lot more information about the key press, allowing your app to behave more appropriately.
You should also be aware of:
1 2 3 4 | @Override public boolean onKeyLongPress(int keyCode, KeyEvent event) { //... } |
onKeyLongPress is called when the key is held down for an extended period. Generally it’s better practice to let the default implementation handle this.
How to be a good Android citizen: play nicely with other Apps
One of the most fundamental differences between Android and iOS is the more advanced multitasking features. Your game needs to appropriately respond appropriately whenever it ceases to be the center of attention and another App takes over.
iOS developers would already be familiar with their games losing attention (or “focus”) whenever the player recieves a phone call. This kind of interruption has much the same flow on Android. The player is playing your game, receives a phone call, and the game politely pauses itself so that when the player returns to the game they are able to pick up where they left off.
Android applications should behave in a very similar matter. When the player receives a phone call, politely pause the game, quickly fade out music, and when the player returns to the game fade the music back in and wait for the player to perform some interaction (such as pressing a “resume” button) before restarting the game. This same workflow applies whenever any other application takes over the screen and has the users focus.
This is rather simple to implement. As soon as your game is no longer in charge, the onPause method with be called. Once the player has returned to your game, the system will nicely call onResume.
1 2 3 4 5 6 7 | @Override protected void onPause() { super.onPause(); RemoveDialogs(); Pause(); } |
1 2 3 4 5 6 7 8 | @Override protected void onResume() { super.onResume(); //Start the game back up //You may want the player to perform a “resume” action //before restarting gameplay } |
On Android you also need to be careful to cover other notifications which do take the players attention away from your game, but don’t call the onPause method. These can be a bit more tricky to handle.
One good trick is to override the onWindowFocusChanged method in your main Activity. This method is called whenever a window gets the players attention.
1 2 3 4 5 6 7 8 9 | @Override public void onWindowFocusChanged(boolean hasFocus) { if (hasFocus) { //this means the player is paying attention to us } else { //something else has the players attention right now } } |
By overriding onPause and onWindowFocusChanged you will be able to catch the vast majority of instances where you would want to pause your game to ensure the best possible player experience.
These methods don’t catch absolutely everything. For example, on the Galaxy Tab the player is able to make an application dock appear over your game, obscuring the bottom 10% of the window. Your game is not informed of this at all.
Unfortunately, for many of these fringe cases you will need to find some kind of workaround that works for your game. In this instance, we were careful to ensure that no critical gameplay occurs on that part of the screen. Such is Android development.