Google I/O 2011: Building Aggressively Compatible Android Games
Articles Blog

Google I/O 2011: Building Aggressively Compatible Android Games

September 11, 2019


Pruett: Hi, folks.
So, this is Building Aggressively
Compatible Android Games. And you’re all
in the right spot. Great.
Nobody got [indistinct]. Hi.
My name is Chris. I am the CEO of a company
called Robot Invader, which is
a mobile game developer for Android
and other mobile platforms. And actually,
up until about a month ago, I was a Google employee. I worked
on the Android team as a developer advocate
for game developers. My role was basically to go
to game developers and say, hey, you should make
a cool game for Android, and here,
here’s how to do it. I’ll help you do it. If you’ve been to previous IOs,
you might have seen me or if you ever, you know,
watch IO sessions on YouTube, I’ve spoke a couple of times
about a game that I built while at Google
called Replica Island. This talk is not about
Replica Island. This talk is about games that are aggressively compatible
across devices. But I think that Replica Island
fits that description and so I’m going to reference it
in my talk and try to, you know, use it
as a case study of how a game can run
on everything. So like I said, the topic today
is device diversity and we all know
that there are a lot of Android devices
out there, and guess what? The common factor is that on every single one
of those devices, every single one
of those devices is owned by somebody who would
like to play video games. And they would probably
like to pay you money to play those video games, and that’s why you should be
interested in this. Replica Island,
I released about a year ago, it’s getting close to 2 million
installs at this point. We’re not quite there yet.
But it’s doing pretty well. The ratings
are pretty good. And I think that,
um, part of the reason that it’s been
as successful as it is is that it runs
on all of these phones. You know, we’ve got
over here on one side, we got the G1. That’s pretty much
the minimum spec. I put the Nexus S
as the high end, although they are
very quickly phones that are coming out
that are faster than that. But that’s the range, right?
It runs on all those phones. It doesn’t just run on
those phones, actually. It runs on
all these phones too. I tried to get this slide
to go faster, and this is the maximum speed that KeyNote would allow me
to show these phones to you. There’s a lot of them. There’s like, 160 or something,
the last time I checked. And actually, that data point
is probably three months old, which is an eon
in this industry. It runs on
all these devices. And actually, this is
not an exhaustive list. There’s quite a number of
Android phones out there today that Replica Island
works fine on. Oh, yeah. It runs on tablets
and TVs and stuff too. I didn’t really plan this. I, of course, I wanted
the game to run on everything but when I wrote the game, you know,
it was designed for a G1. That was the platform
that I was building it on. It just turned out that I made
some correct assumptions and because I made
those correct assumptions, it was very easy to run
on all devices and in a couple of cases,
extend what I’d already written to work on all devices. So today I’ll tell you about
what those assumptions were and how you can replicate
that on your own games. There’s also a bunch of stuff
that I did for Replica Island that isn’t relevant
any longer. For example, I spent a long time
on Java optimization. Java optimization
matters a whole lot if you are targeting a G1. It doesn’t matter very much
anymore for any other device, and there’s two reasons
for that. One, Java has gotten
a whole lot faster with different versions
of Android, and number two, the phones have
gotten a whole lot faster. Actually, I’ll say
three reasons. Number three is,
how many game developers do we actually have
in the room here who are writing games right now? Okay. Okay.
That’s a pretty good number. How many of you
are writing them in Java? How many
are using Dalvik in the Java language
to build your games? Yeah, that’s right. How many of you are using
C++ and NDK, right? Okay. So, that’s why Java optimization
doesn’t matter any longer. And I’m not gonna
talk about it. Dalvik is really fast. If you want to do that, you can. I’ll skip it in this talk. Before we start talking about
diverse diversity, you need to understand how the Android compatibility
program works. So, let’s do another
show of hands. How many people
actually knew that Android has
a compatibility program? Okay.
That’s not bad. What? 30%, maybe? Yeah, Android has
a compatibility program. And if you want to build
an Android device that’s considered compatible,
you have to pay attention to it. This is an open source
program. You can go check the spec out,
you know, online. It’s compatibility.Android.com and you can read through
the whole spec. And any Android phone that’s
gonna be considered compatible has to meet that spec. It’s pretty detailed too. I mean, it gives
a device manufacturer areas where they can make
modifications. Like, say they want to have
a different sized screen. Well, there’s a range
of screen sizes that you can support and still be considered
Android compatible, but it goes into
a lot of detail about what you can
and cannot do. And the purpose of this spec
is to require that people
who are building phones to build phones that are
gonna run applications for Android Market
that are written correctly. So, remember,
I had mentioned, you know, all these phones back here? Well, guess what?
They’re all compatible. Every single one of these has met the Android
compatibility spec. Well, how do we
enforce that? Right? How does–how do you know
if a device has passed this arbitrary
open source spec? Well, Google provides
something called CTS, which is a Compatibility
Test Suite. This is about
20,000 plus tests that a device manufacturer
can download. It’s also open source,
can run on their device and get output from. And that output basically says,
yes, I passed, or no, I did not. And if there were failures,
it says, well, you screwed this up,
and you screwed this up, and you screwed this up. And if the OEM doesn’t go back
and correct those failures, then there’s no chance for them
to access Google services like Android Market
and GMAIL and Maps. These services are not part
of the Android open source. They are given to OEMs on the
condition that they pass CTS. So, if you don’t have
a compatible device, you don’t have any of these
installed. Another way to think about that
from a developer’s perspective is that you’re using
Android Market as your distribution platform, you’re guaranteed that you’re
only going to be distributing to devices that have passed
Google’s 20,000 tests that are required
for compatibility. So, that’s the basis. That’s the bedrock stuff
you need to know before thinking about
how a single binary can be compatible with
all these different devices. I’m gonna say
there’s three steps to aggressive compatibility. The first step is
check your assumptions. I worked with
a lot of developers in my role at Google who had
trouble with compatibility, and it wasn’t because
they wrote bad code and it wasn’t because
the devices were broken. It’s because they originally
wrote that code for some other platform, and they made a bunch
of assumptions that were specific
to that other platform. And when they ported the code
over to Android, they didn’t change
those assumptions, and those assumptions
turned out not to be the same set of assumptions that
you have to make on Android. So, the first point here is
check your assumptions. The second is
follow the rules. We’ll talk about
what these rules are in a little bit of detail. But the main message here
is there are rules that you must maintain
to be compatible, and if you break them you will crash
on certain devices. And the third rule is
manage your spec. I’ll get into a lot of detail
about that. But basically, you can tell
Android Market what kind of requirements
your application has. There’s a secret fourth rule
that’s specific to video games, and I’ll talk about it later, but I’m not gonna tell you
what it is right now. So, let’s talk about
assumptions. There’s a lot of weird hardware
out there, right? I mean, there’s weird hardware
out there right now. And the thing is,
Android devices are coming out really,
really, really fast. So you can be guaranteed
that whatever is normal today, or whatever is weird today,
will be normal tomorrow and something weirder
will have shipped. And I had that idea in mind when I was working on
Replica Island. I didn’t know
what was gonna ship, but I knew that it wasn’t
gonna be exactly the same as what had already shipped. And I made four
basic assumptions that sort of future-proofed my
application for compatibility. You have to understand
that I shipped Replica Island, I want to say
just over a year ago, and most of those phones
on that mega slide were not out yet. Most of those phones shipped
after I released Replica Island. And without me doing anything,
they are still compatible. So, I’m gonna chalk that up to
these sort of basic assumptions that I made. First assumption–there’s
no standard for screen size. Your screen can be any size
and it can be any resolution. Now, there are requirements
in the spec that I mentioned, the compatibility spec, that require physical size
and density, so there’s a range that you’re
gonna be working within, but you can’t be guaranteed
that, say, all of your devices
are gonna match 480×320. That’s what the G1 is. Nor can you be guaranteed
that all devices will match 800×480,
or something like that. There’s basically
no standard at all. And so you need to deal
with that. And the best way
to deal with it is to allow your graphics
to scale. If you’re using OpenGL and if you’ve attended my other
IO sessions in the past– you should be– it’s very easy
to scale your graphics. Very often, it can come down
to a GL viewport call. In my case, I actually change
the GL viewport, and I also scale the content
of my scene a little bit to match the aspect ratio
of the display. You can see here that at
the 480×320 version of the game, you can see a little bit
less to the right than the 800×480 version. A more dramatic example is
the QVGA versus FYVGA versus the game. Same game, same graphics,
but what I do is I look at the size
of the screen at runtime. I have a fixed height
that I want to align to that I’ve fixed
for game design purposes. It’s basically 480 pixels tall
because that’s what the G1 had. I scale everything up
to match that height and then I allow the left
and right sides of the screen to grow or stretch
or stretch or contract. And for my particular
game design, that’s completely acceptable. Allows the game to run
on any device. If I’d been using, if I’d made
a 3D game that has perspective, this would have been
a lot simpler. I could just change the viewport
to match the display, no problem, just a matrix,
no big deal. But if I wasn’t using OpenGL,
the actual Android layout system has all kinds of tools
for scaling graphics and moving things around
and adjusting them. But I think probably most people
in the room here are using OpenGL. Key message is not
that it’s hard but you have to think about it
before you write your game. The other thing
that I did correctly but I would do better next time is I shipped one set of
resolution of graphics and I scale up. And the resolution
that I shipped was HVGA. HVGA scaled up on a wide
VGA display like a Nexus One or Galaxy S. It looks okay. The space, still pretty small
so I can get away with it. When you run Replica Island
on a zoom, you’ll see it looks
a little bit pixilated and that’s because
pretty low resolution art is being scaled up to a
pretty large resolution display. So if I were smarter
when I built this game, what I would have done is made
higher resolution graphics and scaled down
on smaller screens. Ship with, say,
zoom compliant graphics and then plan to scale
everything down, you know, when you run on a G1 or
when you run on the Nexus One. That way, scaling down
always looks good. Scaling up, not so much. Second major assumption
that I made was that the G1 was
going to be the minimum spec. That turned out to be
pretty true. There are two devices
in the world that I know to be slower
than the G1 for games. Those are the HTC Tattoo
and HTC Wildfire, and those devices are slower
because they do not have a GPU. Game still runs
on those devices because Android provides
a software renderer for OpenGL ES1.0 which is what you use
if you use the emulator. But as you know,
if you use the emulator, it’s a little bit slow. So, game is playable
on those devices. It’s a little bit slower
than I’d like, but you know, no big deal. For all intents and purposes,
the G1 is the minimum spec. For the vast majority of devices
out there, it doesn’t get any slower
than that. I assumed that OpenGL, yes,
was going to be the fastest way across all devices
to draw 2D content. This turned out to be true,
but it’s a subtle point. It’s a subtle point because
the way to draw fast 2D graphics on a Nexus One might actually be
to use the CPU. That’s because the Nexus One CPU
is really, really fast. It’s in fact so fast
that it can outrun its GPU for doing 2D blitz. There are actually
a lot of devices that have really fast CPUs
that you can do this on. But it’s only
the high-end devices. I say the Nexus One,
probably the Galaxy S, those are about the two that
I’ve tested, the Nexus S. Right? There are devices out there
that have real fast CPUs and can–you can render
the same thing in software faster than you can
render in hardware. But the reason to use
OpenGL ES is that you want to be
compatible with every device, not just those superfast
CPU devices. And generally, the GPU
is the way to go. It is the fastest path
for rendering 2D concept. I wrote this application
called Sprite Method Test, which is open source. You can search for it if you
want to test this stuff out. But it lets you draw the
same scene with CPU and GPU and, you know,
see what the differences are. I also–this is
my fourth assumption. I assumed that all the devices were gonna kind of be
like the G1. I knew that they were
gonna change and be faster and have different sized
screens, but I assumed that they were
gonna have a trackball and that they were gonna
have single touch displays like the G1 and that they might
have a keyboard or not. You know, that was totally
false, as we know now, right? There’s a whole lot of
different phones out there. And they’ve got a lot of
different interfaces that people are gonna
play with. Some of them like the Nexus One
actually do have a trackball. Some of them
have keyboards. Some of them have little dinky
D-Pads on those keyboards. Some have really nice
multifinger multitouch. Some of them are weird, like
this device up in the top left. That has a track pad
on the back of the display that the Xperia Play
of course, has real game controls,
a D-Pad and buttons. And then there are devices–
so far only one that I know of– like the original Xperia which are problematic
for game developers because it doesn’t have
really any hardware buttons except for back
and menu and home. It doesn’t support
multitouch. So, where do you put
your game controls? If you have a game you can play
with a single finger, you’re all right. But if you are–
if you’re like my game, which is–requires at least
motion and jump, you know, how do you deal with that? Well, a solution for me was to
provide customizable controls. This is a solution
I did not come to until after I’d shipped the game
’cause like I said, a lot of those devices came out
after I shipped Replica Island. But in all of the updates
I’ve made since shipping have been to add more
customizable controls. So now if you download
Replica Island and go into the options, you can set up how you want
to control the game with a lot of granularity. Breaks down into
four major spots. There’s the trackball support, which is the original version
of the controls that I wrote. There’s keyboard support
which gives me access to things like the Xperia Play
without any changes. Virtual pad which are for just buttons on the screen
but requires multitouch. And Tilt for devices like
the original Xperia that only have single touch. And between these four, I pretty much
seem to have covered all of the devices
in the world. Like I said, as far as I know,
all devices out there that are compatible and have
Android Market installed you can play this game on. The original control scheme
that I came up with was designed around the idea that some displays
are gonna be single touch. So I had the trackball
for motion and I have these two mutually
exclusive buttons on the screen for jump and attack, and that way, you never have to
have two fingers on the screen. But nowadays, you know, now we know that there’s
a lot more devices out there that have all kinds of
multitouch points. So it’s probably important
to talk a little bit about the different types
of multitouch screens that are out there. Multitouch screens basically
come in three flavors. There’s single touch.
Right? Multitouch. And then there’s what I call
crappy multitouch. And crappy multitouch
is what my best friend, the Nexus One, has. Crappy multitouch
is a multitouch display that can–that can detect
two fingers at the same time. But when those pointers
are moving, it can swap the components
of one pointer for the other in a very specific case. And that case is
when the two pointers cross the same horizontal
or vertical axis. So the caonical problem case
for this type of display is if you have a finger
on one side of the display and another finger on
the other side of the display and you drag them across, at that vertical line
where they pass you may get
component swapping. So, your X from pointer one
may suddenly become your X from pointer two. And if you’re drawing a line
on the display where those fingers are,
you’ll see them go like this, which is really frustrating
for game developers because if you want to have
a dual stick control scheme– which I recommend
you not do anyway– but if you wanted to have
a dual stick control scheme, you know, that’s gonna be
two fingers that are passing
the same horizontal axis, and you’re gonna get
component swapping, and it’s gonna be
very difficult to control. So you can still deal with
crappy multitouch displays. My solution was just to get rid
of all vertical motion in the fingers…
you know, I have a slider that moves left and right
with one finger and buttons
on the other side. And there’s
this horizontal line that they are both on, but they never move vertically
to cross it. And so, this actually works
pretty well on the Nexus One. In fact, when I showed it
to some colleagues who were aware of the crappy
multitouch problem, they were impressed that I was actually able
to get this to work. They thought I’d done some
complicated heuristic, you know, to unswap my control
points or something. Nothing like that. Just made sure that there was
no vertical motion. Couple of other tricks
related to screens. The back and menu keys
on a lot of devices like, again, my friend, the Nexus One,
are flush with the display. And that means
if you have buttons on the right side
of the display or you’re gonna use
a trackball for control, it’s pretty easy
to slip off the track pad and hit the back or menu
buttons. And that’s a pretty bad
user interface because usually those buttons
take you out of the game. So, for Replica Island,
I ignore those buttons if they happen to occur
within 400 milliseconds of a game event. A game event is the user
touched the screen or the user touched
the trackball. So that means if the user’s
touching the screen and they slide off
and they hit the back button, nothing is gonna happen, ’cause I’m gonna ignore
that back button. But if they pick
their finger up and they put it back down
on the back button, that actually takes longer
than 400 milliseconds. So the back when we read it,
it’ll be fine. There’s no mis-clicking. You should also be aware
that these devices have really small buses,
right? The memory bandwidth
is not good. That means loading your texture
to VRAM takes time, and you probably can’t do it
at runtime. One solution to that is to use
texture compression. There’s about four types
of texture compression in the world
out there today. There’s ATITC, which
is proprietary to ATI devices– Snap Dragon, okay,
things like that, stuff that Qualcomm makes
mostly. There’s PVRTC, which is
proprietary to power VR devices like the Droid. There is DXT which you’ll find
in video devices like the Zoom,
also proprietary. There’s ETC1. ETC1 is a non-proprietary format
that’s supported by all devices that support OpenGL ES2.0. So it’s great.
You can use it. Its fatal flaw is
that it doesn’t support Alpha. You can’t have
an Alpha Channel or do any sort of transparency
with ETC1. So, some developers are
really tricky. Right? Some developers have shown me
that they take their Colormap and they take their Alpha map
and they save it as two different textures
and they put them back together in the shader. Solved. Okay. You know, or you can
choose to do what I did which is just not compress
your textures. I was actually able to fit
the whole game uncompressed in VRAM, so didn’t
have to worry about it. But if you are not like me,
and you’re worried about fitting into VRAM
on all devices, you should be aware that texture
compression is device specific. Now, there is a little solution
that’s better than ETC1 that we’ll talk about
in a little bit. But you should know that
if you do decide to use ETC1, OpenGL ES represents a 2.0,
which is what is required. It represents the vast majority
of devices out there, over–greater than 70%. There’s some question marks
on this slide because the parsing tool we used
to generate this data, like, totally failed. But even if we assigned
OpenGL ES1.0 to that question mark part, we could see that the vast
majority of devices are 2.0 capable. So if you want to rely on
something like ETC1, you can safely do it. Same goes for
OpenGL extensions. Extensions are a system by which the OpenGL spec
can be extended. They’re optional. Some hardware’s going to
support them, some is not. There’s a GL extension string
which contains at runtime which extensions
a current device supports, and you can query that. You should definitely do it
before using any extensions, otherwise you may
crash on a device that doesn’t support the
extensions you want to use. I used two in Replica Island, Draw Texture
and Vertex Buffer Objects. Both of those are optional, and support for them
seems to be universal. But I still check for them
just in case. So, let’s move on
to rules. We talked about assumptions
you need to–you can make, and those assumptions
that you cannot make to build a compatible game. Now, what are the rules? What are the borderline things
you should totally not do? Probably, at least three
other people at Google IO have talked about this
because it ended up affecting a lot of apps
when the Zoom came out. But if you didn’t know, it turns out that devices have
different default orientations. If you’re holding a tablet, its default orientation
is landscape, and if you have a phone or
something like the Galaxy Tab, its default orientation
is portrait. And you’re like,
like, why do I care, right? Because when you
run your game, you’re gonna just
set the orientation to whatever you want,
and that’s what’s gonna display. Well, you care because
accelerometer data that you get
out of the hardware is relative to the default
orientation of the device. So if you have any sort of
tilt or orientation controls in your game, and your method
is just to suck those out of the hardware
and say, okay, it looks like Y decreased– that means the user
is tilting left– you’re gonna be
sorely disappointed when you run on a device that has a different
default orientation because your controls
will all be 90 degrees off. This is really easy to fix
once you know about it. In fact, NVIDIA
has provided a useful function to just convert between the device specific
accelerometer data and a canonical screen space
accelerometer version. And this is my sort of
Java language version of their function. But if you check out their
Tegra Zone developer area on their website, you can find the original
C++ version. I just dropped this
into Replica Island, and it solved all my problems.
It was great. Another rule–
if you’re using JNI, which is
the Java Native Interface, that’s the system by which you
call from some other language, like the Java language, through Dalvik
into native code. If you’re using it,
you need to be careful because it’s a little fragile. In particular,
the JNIEnv variable, which comes down
with every callback, you can’t cache that. It changes every time,
or it could potentially change every time you make
that callback. And it could change
if the callback is called from a different thread. And just for a lot of fun,
if you cache it, and if you use the wrong
environment variable in the wrong context, you will get extremely difficult
to debug crashes, and they won’t be universal. They’ll be things like
horrible race conditions that work on some devices
and not others. Don’t ever cache
this variable. I worked with one
very well known developer who had released
a popular Android game, and it worked
on all devices except for one. And the developer
was positive that that device was broken
because… it just didn’t work
on that thing. Everything else worked. Just the one device…
didn’t work on. And we debugged it together and when we got
to the bottom of it, it turned out that he was
caching this variable and on that particular device,
there’s a race condition where, you know, thread one won
before thread two and it caused a crash.
On every other device, it happened to be thread two
won before thread one. By not caching this variable
any longer and just passing it through
when and he got a callback, problems went away. I think this is old news
for most people here, so I won’t spend
too long on it. But there’s different
versions of Android. Each new version of Android
introduces new APIs. If you’re going to be
backwards compatible across a bunch of different
versions of Android, you need to only call APIs
that are available in your minimum version. So you know, you go
into AndroidManifest and you say, oh,
I have min SDK S3. That means I can run on
Android 1.5 or higher. But I compiled against eight,
which is Android 2.2, I believe. So what happens
if you want to call a function that was added
in Android 2.2? Well, if you just call it,
it’ll compile okay. But when you run on the G1
or that other 1.5 device, it’s gonna crash. If you use Dalvik reflection–
or you could even branch on the build ID
or the version ID– just be careful not to call
methods that may not exist in your minimum supported
system version. And actually, the docs
are really useful for this because they will show you
exactly how– exactly which version every function
in the Android API was added. Another rule. You should be frugal
with your RAM use. If you are writing
to Dalvik, you have a fixed size heap
and you know what it’s gonna be. On a minimum spec,
it’s gonna be 16 megabytes and on newer devices,
it’s much larger than that. So you can deal with
that memory stuff pretty easily. If you are allocating
from native code, which most of you said you are,
there is no fixed size heap. You can allocate as much memory
as you’d like until the device runs out
of RAM, which is great, right? Because you’re not
limited to 16 megabytes. But it also means that if your
application needs 100 megabytes to run at its high watermark
and your user is on a phone that doesn’t quite have
that much free, you may run into
an out-of-memory situation that you didn’t expect. So the message here is be
frugal and fail gracefully. Try to fit into as little
runtime memory as possible. For reference, Replica Island
runs at 6 megs. Keep your application
as small as possible. Older phones, especially the G1,
but some other older phones have very little
internal flash. You should also absolutely
support the apps to SD configuration
in your Android manifest because that’ll let people
to move the application to the SD card. You don’t want to be
non-compatible with a device just because you just don’t have
enough space to install it. If you are coding in C++, you may have considered
using Neon. Neon is a special architecture
instruction set that allows the optimization of certain
floating point operations. Now, Neon is only supported
on devices that have RMV7 chipsets. But the trick is
not all RMV7 chipsets support Neon. So if you assumed that by
building against RMV7 you are guaranteed Neon support,
you are wrong. For example, the Zoom
does not support Neon. Now, there is a library
that comes with the NDK called CPU Features
which you can use to check at runtime, whether or not this particular
instruction set is available. But do that. Make sure you check. Otherwise, it will crash
on the device [indistinct]. And, you know, I think
everybody here knows this because we pounded it into
their heads at every Google IO, but don’t call on
documented code. Don’t use private APIs. You can go into the Android
open source tree and you can find a bunch
of interesting-looking functions and you can find tricky ways
to call them from your code, and if you do that, you guarantee that your game
will crash at least– if not now, then in the future,
when those APIs change. APIs that are private
are private because they’re
likely to change. They’re not ready
for primetime. And if you rely upon them,
you will crash. So the main message here
is lots of diversity. Right? I mean,
there’s lots of rules. There’s lots of flexibility and this is probably
the point in the lecture where everybody in the room
is like, oh, my God. How am I gonna deal with this? This sounds horrible. Like, for example, maybe you have some weirdo
control scheme that only works
on a subset of devices. Right? Like, maybe you require
discreet multitouch which is the non-crappy
multitouch. Or more likely,
maybe you just want to use OpenGL ES2.0
because you want to write your whole graphics back end
in shaders, and there’s no way that’s ever
gonna work on a device that doesn’t support
OpenGL ES2.0. What are you gonna do,
right? That’s the segue
into our next talk, and the segue itself is that
Market has a solution for you and it’s called
AndroidManifest.xml. So, what is
AndroidManifest.xml? I mean, everybody fills it out
when you build your application. You put your app name in there and you describe your activities
and stuff. And it’s metadata
about your application, but what is it really? As far as Market is concerned,
it’s your minimum spec. For example, say we say,
oh, I require this feature called Android.Hardware.
Touchscreen.Multitouch. And I really do require it, ’cause I put the required field
to true. That means I cannot run– this application is not
compatible with devices that do not support
multitouch. And if you put this
in your manifest and you upload it
to Android Market, Android Market will not
distribute it to devices that don’t meet
that requirement. So, your G1 users are not
even gonna be able to find your app
in Android Market. It’s just not there. If you use the web interface, find the app and try to
install it to your G1, it’ll say, oh, sorry. Your phone is not
high enough spec. It doesn’t meet the requirements
set by this application. There’s tons of stuff you can
require in Android manifest. This is why I call it
a minimum spec. There’s all kinds of
requirements you can put in here. Here’s a little taste. Say you want to
only ship to phones that have auto focusing cameras. Okay, you can do that. Or say you want to ship
to phones that support Wi-Fi. You might think that all phones
support Wi-Fi, but it’s probably
an optional part of the spec. So maybe if you require it, you won’t be getting
user reports from your users who are on the crazy
non-Wi-Fi supporting device that comes out in six months. Or here’s a more common
requirement for game developers. Say, you do want to use one
of those proprietary texture formats
that I talked about. You can require it. You can say,
well, you know, I know I’m gonna cut out
some of my users but I want to use
PVRTC. Put this in your manifest and Market will not ship
your game to a device that can’t support that version
of texture compression. We talked about
crappy multitouch. You can cut out crappy
multitouch devices. Say, I’m sorry, I have my
dual sticks and I like it, and I don’t want to even want to
deal with you Nexus One users. Whatever. However many
other crappy multitouch devices might be out there. Set this stuff
to require it, and they will never see it
at Market. By the way, if you said
require to false here, it means
that you want that feature but you can deal with it
at runtime. You can say, oh, well,
I really wanted multitouch, but since you don’t have it,
here’s another configuration you can use,
some other way. If you set it to required,
Market will be real strict about how it filters
your application. Couple other examples. We talked about requiring
a minimum SDK version and a maximum SDK version. You could also require
combinations of different screen size
and density. That’s physical size,
physical density, and this is
the most common one. If you want to require
OpenGL ES2.0, that’s that one at the bottom,
you just say, well, I need an open
OpenGL ES2.0 and those old devices
won’t appear. So by carefully
managing your spec, you can effectively
cut out devices that you don’t want to manage. You know, we talked about
a lot of assumptions and a lot of ground rules, and
a lot of them have to do with, well, there’s a device
over here that’s this, and there’s a device
over here that’s this. And if you would like to say,
you know, I don’t even care
about this stuff over here, I only want to focus
on these guys– AndroidManifest and Android
Market will let you do that. If you think about this, this is actually
really powerful. Because if we look at
the low end and the high end– what I’m gonna call the G1
and the Zoom are probably the good
representations of those two parts
of the spectrum– that’s a pretty big delta
like in terms of– let’s just talk about
performance. That’s a pretty big
performance delta. Right? But let’s say we require
OpenGL ES2.0, say in our manifest. Now we have to have
OpenGL ES2.0. Now the delta is
a lot smaller. Now the minimum spec
is the Droid, because that was
the first device to ship with
OpenGL ES2.0. Doesn’t even matter
if you use OpenGL. Your whole thing might be
like this 2D CPU game. Who cares? If you say in manifest,
in the manifest file, that you require OpenGL ES2.0,
it will cut out the low end. You know, the caveat
to this approach, right, is that every time
you do this, you are cutting out
some number of users, right? The size of your user base
is getting smaller. And of course, you know, in addition to writing
awesome games, we’d probably all like to
also, you know, make money
off of those games. So having more users
is always a good thing. So it’s in your interest to try
to be as compatible as possible. But when there’s
an area of diversity that’s just too open for you, you can put your foot down
with manifest and require something
in the spec. Here’s the spec
for Replica Island. I don’t really require anything. I work on all screen sizes. I run on everything
Android 1.5 and higher. Now because I’m able to support
such a wide variety of devices, you can see that
in my install information, I have more Android 1.5 users, a lot more than what’s normal
for this category. I have 15%
versus the standard, which is 4%. And I believe that this is
because if you are a… user has a two-year-old device and it’s only got
Android 1.5 on it and you go to Android Market
looking for some games to play, it’s a little bit slim pickings,
you know. Like, most developers
have moved on and said, well, I’m gonna require
Android 2.0 or I’m gonna require
Android 1.6. But since I was able to support
those users, that makes my app very visible, and so as a result
I have a lot of 1.5 users. It’s up to you. If you want to
support everybody or you only want to
support a subset, you can control that
with AndroidManifest.xml. Okay, now, I want to talk about
the secret fourth rule. This is specific to games. This is a subtle point
that I did not understand until after I shipped
Replica Island. It is true that these devices
are very different. It’s true that there’s a lot
of device diversity. It’s also true
that you can solve it. There’s Dalvik reflection, there’s scaling your graphics,
there’s, you know, using the Android APIs
to do automatic scaling for you or adding customizable controls,
things like that. Those are all
technical problems. You can solve them. You sit down,
write some more code. It’ll work. Much harder problem
is that your users are also gonna be
a very diverse group. It used to be– you know, my background
before I went to Google was writing
console games. So, I wrote a lot of games
for Game Boy Advance. Game Boy Advance,
you pretty much know who your target group is. It’s kids between the age of
6 and 12. Right? Or if you’re gonna write
an Xbox 360 game, you can be
pretty much guaranteed that your target audience
is males between 15 and 30. All right? Just by the market segment that
the console itself controls. But on a phone, I mean,
everybody’s got a phone. Right? What do you know
about your user? How much do you know
about what they want or what kind of games
they want to play? You know very little. I found out
that when I added all this customization
for controls, what surprised me about it
was that users who didn’t need to
customize their controls, because I’d already
provided a solution for them, were going in
and customizing them anyway. You know, I added
like the tilt stuff in there for the basically
the Xperia users. And I kind of think
that that’s a difficult control scheme to use. But I had users telling me
that they preferred tilt over the original
trackball control scheme even though they had
like an Nexus One or a G1. That surprised me. The user was different,
not just the device. You know,
in designing Replica Island, I had some idea that users
are gonna want different things. So in the game design,
I tried to provide different aspects
of interestingness, different poles by which
people could, you know, become interested
in the game. One of those things
was just a game play, right? Some users are gonna
like crushing stuff and moving through the level and trying to
figure out the puzzle and making it
to the next level. That’s the whole game
for them. That’s enough.
Right? So try to make that part good. Another aspect of
interestingness was a story, right? Some people might not care very much
about crushing stuff, but they want to find out
who they can trust or who’s the real bad guy?
What’s the secret ending? You know, art style
was another thing. I tried to go for a retro
16-bit game look even though the game
plays very differently than a retro game,
because I thought that that would draw
some users in. That’s what they’re gonna be
interested in. And recently,
I’ve actually added explicit difficulty
settings for new users. I had some dynamic difficulty
adjustment in there before, but it turns out that the user
base is so wide here that I can make
a much better game if the user
tells me upfront what kind of game
they’d like to play. Some people want to put
their pride on the line. They want a challenge
and they want to feel awesome when they’ve completed
that challenge. And other people are like,
you know what? I’m just gonna play this
for five minutes. I don’t really want
to play the same level over and over
and over again. I just want to
sort of glide through. So by providing specific
difficulty levels, I was able to access
not just more devices, but more users. One thing I didn’t have to do
but you should think about is I did not provide an option to customize
the graphics quality. If you want to customize the
graphics quality in your game and you’re using OpenGL,
it should be really easy. It’s one call to the
SurfaceHolder.setFixedFunction. Sorry.
setFixedSize method. What that does is
it makes your window that you’re rendering to smaller
than the window of the display, and then the system
will automatically scale it up. So the effect is
you’re filling fewer pixels, and actually fill is the part
that’s slow on these devices. So you’re filling
fewer pixels, but it still takes up
the whole screen. The graphics
look chunkier. I don’t know
if you can tell, but on the left
is native resolution and on the right is 50%
reduced and scaled back up. It does look
a lot chunkier. Although at 75%
most users probably can’t tell. If you need to get
some speed back or you want to give
the user an option to dial down
the graphics quality because maybe they’re on a phone
that is lower quality than you anticipated,
you can give them an option to set this value,
and that would let them play even if they have
kind of a crappy phone. So the goal is
target all devices, right? That’s a technical problem.
You can deal with it. I did.
I kind of got lucky, but you follow the assumptions
that we talked about today and the rules
that we talked about today, and, you know,
I think any game that’s on
the Android Market today can be aggressively
compatible. Much harder is to understand
your user base. Building a game
that is compatible across
a large number of people is actually
a very difficult problem. And it’s not just
about controls, and it’s not just
about game content. So I urge you when building
Android games to think not just about the
technical side of compatibility but also who your users are. You know,
who is this market? And what do they
want to play? And give them options
to tune the game the way that they like. And I think if you do that,
you have a lot more– bigger chance of success. This is the end
of my remarks. I sped through it
a little bit ’cause we’re running
a little late, but we do have time
for some questions. If you’re interested
in what I’m up to next, my company is called
Robot Invader. We’re at robotinvader.com. At Twitter,
I’m at c_pruett. And we don’t have
anything to show quite yet. We’re only a month old. But we will have
some pretty cool stuff. So please stay tuned. If you’d like to ask questions, please come up
to one of these microphones. Thank you very much. [applause] man: You mentioned
putting restrictions in the manifest to limit
the number of devices and there was
the required true-false. It’s obvious what happens
when it’s true. Well, how does the Market
respond to that requirement when it’s required
to be false? Are you just saying,
I would like this but I can deal with it? What’s the point of just– what’s the point
of even saying that? Pruett: Right.
So, the question is about what does
the true-false thing do? Now, I believe you said
that the false market will not treat it
as a strict filter so it will allow devices–
it’ll basically ignore it. But the purpose of the manifest
is not just Android Market. Right? The manifest is supposed
to be all metadata about your application. And Android applications are
designed to be self-describing. So you should be able to look
at the binary, understand what kind of devices
you can install this on. If you set that requirement
to true, I don’t think you’ll even be able to install
like over ADB… man: Right.
Pruett: a device–or application that doesn’t meet
the requirements of that phone. So I think that if you have
the field set to false, you’re just giving Android more
data about your application, whether or not
it acts upon it or now, or maybe in the future,
I don’t know. I don’t think that Market
in particular does anything with it
if you set it to false. But it’s probably a good idea
to give your… give Android or Market
or any of those systems as much information about
what you want as possible. man: Okay.
Pruett: Over here. man: So when you parse
the extension string– Pruett: Yes.
man: If you come across devices that don’t support
the extensions that you need, how do you fail gracefully? Do you just tell them
you can’t do it? Or do you have a backup?
Pruett: Sure. Depends on the extension. But, generally,
extensions are fast paths for stuff
that’s in the spec already. So, for example, draw texture
just takes a texture and blitz it, you know,
access-oriented to the screen at the scale
that you specify. Now, if draw texture
is not available, you could fall back on a quad that’s orthographically
projected. It’s the same thing. man: Is that what you do, or…
Pruett: That’s what I do. man: Yeah.
Pruett: Yes. Or another example
is vertex buffer objects. Fast path for regular
vertex buffers, right? Now,
there are some things like you will see
the texture compression formats that are supported
in the GL extension string. And if your textures
are all in some format that that device doesn’t support,
I don’t know what you can do. You can try to
ranscode them, but I think you’re
pretty much up a creek. So, that’s probably
where you want to start putting requirements
in the manifest. man:
Right. Thanks. man: Before I ask my question
to play off his a little bit, then that’s assuming you’re
not filtering in your manifest. Pruett: That’s right.
man: Therefore… not filtering in the Market.
Pruett: That’s right. man: And then you would want
to handle it at runtime. Pruett: That’s right.
So when I– one developer
that I spoke with was interested in transcoding
between ATITC and DXT because those formats are
actually at a binary level pretty similar. So there might be like
tricky things you could do. Like, oh, I encoded
all my stuff in DXT but I’m finding out, I’m actually running
on an ATITC device. At runtime, I can transcode
this or something. But probably what most
developers are gonna do is not anything
that complicated. They’re either just gonna
use a non-proprietary format like ATC1, or they’re gonna require
their manifest. man: Okay.
So, my question. Say I’m really motivated
and a little bit crazy and I want to do a game
or a graphical app that’s really, you know, it’s taking advantage
of OpenGL ES2.0. It’s targeted at, say,
tablets. It’s really high end.
Pruett: Hmm-mm. man: But I also want to appeal
to the lower-end devices. Would I, I guess,
in more general terms, would I develop two separate
apps with different manifests just so that it could appear
to any given user to be the same app
but only be exposed to the devices
that are necessary? Pruett: You could.
You shouldn’t. Yeah, I mean, you certainly
have the ability to do that. The problem
with that approach is that then you’ll have
two apps on Market. They will have two different
sets of ratings information. They’ll have to have
slightly different names. And if one of your apps
gets voted way up, it won’t change the rating
of the other application. man: What’s the right way
to do that? Pruett:
The right way to do that is preferably to have a single
binary that you can do both in. I mean, what would you change
between the low-end version and the high-end version? Well, you’d probably change
the resolution of the textures. You can do that
at runtime. If you were gonna switch
between, say, a fixed function renderer
and a shader renderer, that actually might be
quite a lot of work. But it’s also you know, a class
you can swap out at runtime. The trick is…to understand
that you can control, you know, which devices get your
application based on the spec. But if at all possible,
your ideal scenario is a single binary
that supports as wide a range of devices
as possible. man: So then the concept of
a minimum spec in the manifest gets real tricky because
it may not so much be minimum as I can do this
with a low-end device but otherwise I can do this
with a really high-end device. How do you filter? Pruett: So in that case,
you don’t need to filter, right? If you can actually span
whatever you’re doing, if you can run on the low-end
device and the high-end device, you don’t need to tell
the manifest anything. You’re available to everybody.
Right? That’s a runtime
computation. So what the manifest is for
is for areas where you couldn’t make
any concessions at runtime. You can’t just degrade
the graphics quality or you know,
resize the textures. Like, if you have
a shader based rendering engine and you don’t care
about fixed function, you know, rendering engines,
you can require OpenGL ES2.0, and then those devices
just won’t get it. Does that make sense?
man: I think so. Thank you.
Pruett: Thank you. man: A little bit more
of a gut question. Pruett:
Sure. man: How do you balance
those factors when you want to make
those changes like you did for the difficulty settings
and really you know, deciding, okay, well, this market
isn’t big enough. I want to include
lower-end devices. What do you use?
Hard data? You know,
do you get feedback? Pruett:
Yeah. man: You know, are you looking
at the competition? You know,
those are the tough choices where you decide, okay, now I’m gonna spend
two extra weeks working on this. Pruett:
That’s a good question. In my case, the game was written
against the low end. So all I had to do
was scale upwards, and that wasn’t very hard. In fact, it pretty much
worked out without me doing anything. If you have shipped something,
and now you go back and decide, well, you know,
I shipped something and I required OpenGL ES2.0,
but I don’t really need that and boy, there’s
a lot of users out there who have older phones
I’d like to support, That’s a little bit trickier. Google does publish information
at developer.android.com. about the distribution
of screen sizes and also of Android
installed versions. So you can get a sense
from that data, you know, what the whole landscape
looks like. You can also use…
there’s a bunch of metrics that just got added
a couple of months ago to Android Market to see
who your users really are. So if you got complaints
that like, oh, it crashes
on wide VGA displays and you went and you looked
and saw that 30% of users have wide VGA displays, that’s probably an important
bug that you want to fix. Above and beyond that,
I highly recommend that you do
your own analytics. You know,
actually Android can– there’s an Android version
of Google Analytics which you can plug in. It’s about
three lines of code. Super simple. You can send whatever data
you like back and it’ll aggregate you
on a webpage and give you graphs
and all kinds of information. And it’ll tell you
in excruciating detail, depending on what values
you send back, who your users are and
what they’re doing, you know. I worked with a developer
who tracks how long it takes for users to complete
each level. And they found out
that everybody completes the first level, and 80% of users
complete the second level and 10% of users
complete the third level. Okay. There’s a problem
with the third level, right? Doing your own analytic tracking
is super useful anyway. That said, if you are talking
about a group of users that you’re not
shipping to now and you don’t know
how large they are, it’s pretty hard to tell,
because absent data which only aggressively
compatible applications have, you kind of have to either take
a chance and ship something and try to make it
as compatible as possible and ship it
and see what happens. or try to rely
on third party data. You know,
in addition to Google, there’s a flurry
and there’s, you know, AdMob and other people
who will publish data as well help you
make those decisions. My suggestion would be to,
you know, choose a minimum spec
as low down as possible, you know, try to make
your minimum spec as old a phone
as you possibly can and then work from there. man: Hey, Chris.
Pruett: What’s going on? man:
Everything’s been pretty good for us native developers
for ARM so far, but Intel announced
maybe a month ago that they’re gonna start
doing X86-Android. Pruett:
Sure. man: So what do you recommend
we do for compatibility for you know, ARM? Because right now, as it stands,
my native games are I’m gonna have to do
something quickly. Pruett:
Sure. Sure. Well, you know, the NDK will have to support ARM.
or I’m sorry– other architectures like X86– before you can
really do anything. If you look at the NDK, there’s actually already
some preliminary support for X86 chain in there. And what’s gonna happen
when that support shows up or support for any other
architecture, say, you know, like a power PC device comes out
tomorrow or something, right? The way it’s gonna work
is your… if you look inside
your application that you built with
the NDK, you end up with a folder
called libraries and in that folder,
you have the output of your compilation,
your libraries. And actually, you can have
multiples versions for different architectures. So, right now,
you can choose to compile against both ARM 5
and ARM 7. And it will automatically
load the correct one. In the future,
what I would expect is that you hit the same
indicate build command and if you set
your make file up correctly, you will get a library
for X86 as well as for ARM. Now, that makes a lot of
assumptions, right? It assumes that your code
is first of all just gonna compile against
an X86 code base and that your, you know, data is a little [indistinct]
or whatever. But I think that functionally
the way it’ll work is that your application– it’s already a sort of
a fat binary, right? It’s already able to
support multiple architectures, and they will just add
architectures onto that. man:
But do you think the Market– that makes perfect sense. But do you think
the Market is gonna have an opt in
or an opt out? Because the one thing I don’t
want to have happen is, you know, all the new
X86 devices roll out, and just we’re crashing
on them. Pruett: Right.
That’s actually already there. If you…one thing I didn’t
mention in this talk is that Market will
look at your manifest to figure out its filters,
but it also draws filters from intrinsic information
about your application. So for example, if you target
ARM v7 right now, you will not appear
on ARM v5 devices ’cause you said I can’t support
ARM v5 by nature of you compiling only against
ARM v7. So, if you have
a native application and some other architecture
shifts, by default, you’re not gonna show up
on that device until you explicitly
go back and recompile and then ship an update. Did that answer
your question? man:
Oh, yeah. Pruett: Okay. Are there
any other questions? If not, thank you very much
for coming. Appreciate it.

Only registered users can comment.

  1. Chris,
    Thanks so much for share your experience plus all this tips with us and for all your support as well!

    Good Luck Bro with your new Company…
    Hope you can still be able to help us, once in a while maybe?!?!
    Thanks so much…

Leave a Reply

Your email address will not be published. Required fields are marked *