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 480x320.
That's what the G1 is.
Nor can you be guaranteed
that all devices
will match 800x480,
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 480x320 version of the game,
you can see a little bit
less to the right
than the 800x480 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.
