COLT MCANLIS: Everybody.
My name is Colt McAnlis.
I'm a game developer advocate here at Google, focusing on
Native Client and Chrome.
Thank you all for coming to the Google Dev Day.
We've got a lot of great talks so far today and hopefully
this will continue in our processes to get you ready to
use web gaming, I suppose is the big thing.
Here at GDC, there's a lot of focus on console, but everyone
is moving towards web gaming.
And we understand that this is an important trend in sort of
the evolution of gaming as a whole.
And what we're going to talk about today is best practices
in developing those web gaming.
So being at Google, you sort of have an interesting ability
to look at the broad spectrum of data and the number of
developers out there on the web doing some amazing things,
and mine that data, look at it from different angles, and
sort of construct the best practices document.
And that's really what this talk is about.
This doc very specifically is focused to developers who are
not already multi-millionaires at developing web games.
If you're already if you're a making millions and millions
of dollars with your web game, chances are you know
everything that I'm about to talk about.
So feel free, check your emails.
Zone out.
Figure out what parties you're going to at GDC.
Have fun with that.
In mean time for everybody else, we're going to talk
today about developing a web game in modern browsers.
Now the interesting point here is twofold.
First off is I'm saying in modern browsers.
So I think we can all agree that when we talk about web
gaming, there's sort of this threshold of minimum feature
set that we're not willing to go below.
A lot of developers when you talk about web gaming and they
say well, what about ubiquity?
What about reach?
But at the same time, they're not really able to make the
experience of their game translate to these older
browsers that don't have the same capabilities.
So today, I want to make sure that we're focusing things
since you are at the Google Developer Day, that we're
actually talking about Chrome.
Chrome is considered a modern browser.
It's a very important linchpin in the
pushing of things forward.
Google has a lot of vested interest in making sure that
this is a platform that we can continue to push the internet
forward in positive ways, and basically enrich the ecosystem
for all of humanity.
Because pretty much everyone uses the internet nowadays.
And if you don't use the internet, hopefully
we'll fix that soon.
Anyhow, let's get started.
So let's start from the top.
If you're a console developer and you've never shipped a web
game, you have no idea we're talking about.
And you generally have no idea what entails web games.
So let's talk about that for a minute.
So first off, a web game generally have
some sort of client.
The client is downloaded from a server to the client to the
end user's machine in a web page that does some
interesting things.
On the end user's machine, it usually does some sort of
scripting and displays.
This means code execution as well as visual display--
2D, 3D, or whatnot.
In addition to that, this is served--
the client, including the scripting data and all the
information is to display--
is served from some sort of content server.
This could be a web server or a
scalable distributed network.
And it also has some relational
context to a database.
This is because most people nowadays in web gaming, they
require you to log in.
They store your saved game state in the cloud somewhere--
have you purchase an item, who's your friends, have you
invited this person, what level are you.
This information is all stored in some
sort of large database.
And I could rattle off a bunch of acronyms about what
databases are and what databases aren't, but we'll
stick to just saying that clients
communicate with databases.
Once you've got your content server to distribute your
content to your client side and your database to sort of
understand the relation to the user and the universe that
you're trying to generate, some games also have a game
play server.
Now a game play server is really the realm of what we
call AAA niche gaming.
So this is things like first person shooters, real time
strategy games, things that run at 60 Hertz on some
ungodly monitor that you need some computer from the future
to generally be able to run on your local machine.
These games effectively require some piece of hardware
running in some server farm actually running the
simulation of the game to keep all of the clients in sync at
the proper frequencies.
Now some games require this on the web.
Some don't.
But it's important to point this out here.
So once you've got this working, sooner or later
you're going to want a social graph.
This is the real power of the web.
When you start talking about what's the difference between
a web game and a console game and a PC game that you go buy
from a retailer, what you're really looking at is that a
web game gives you direct access to a social graph in an
immersive manner that you can't get
in those other relations.
And then once you have all of this, the final thing a web
game does is monetize.
At the end of the day, you guys gotta make money.
We've all got significant others and mother-in-laws who
say we should get real jobs instead of making video games
for a living.
We want to prove them wrong by monetizing effectively and
making it rain with dollar bills.
That's sort of the goal.
The good news here is that as Google as a platform provider,
we've actually got software services in place to handle
and address all of these needs for a web game on your behalf.
And the rest of the talk, we're going to be talking
about the best practices in doing all of these
items you see here.
But we're going to do it from the context of the Google
services, mainly because of well, obviously we want you to
use our services.
We think that they're fantastic.
But also, it just sort of gives us a line in the sand as
far as documentation is concerned.
We want to make sure that we can provide the documentation
for the ideas that we're talking about.
So then our content databases, what we're going to talk about
today is Google App Engine.
It provides you both content distribution for web pages and
static data, dynamic data, everything you need, as well
as a relational database.
You actually store your table information,
players and what not.
For social graph, we've got Google+.
If you don't have a Google+ account, go get one now.
Make sure you sign up by the end of the talk so then you
can follow me on Google+ where I spend most of my time
posting about web gaming and Native Client development.
And for monetization, we've got this great integration
point called Google Wallet which is super easy to use.
It has a lot of benefits that we'll talk to you towards the
end of the slide.
In addition to these right here, we've also got a couple
others that you should be interested in the we'll talk
about today.
The first is Google Analytics.
Google Analytics is typically designed as a way to track
your users' movements and who visits your website, where
they come from, how they're using your website, what's
wrong, what's broken, what's popular, and whatnot.
But this could actually be modified with a few APIs to
work very well for games.
We'll talk about how you use that.
And then finally is the Chrome Web Store.
Chrome Web Store is an amazing sort of outlet that Google has
launched about a year and a half ago now that provides
sort of a content discovery portal for all things web.
We've got an amazing plethora of applications both
games and not games.
But if you're looking for an amazing place for users to
develop your content in a curated, informative way,
definitely check out the Chrome Web Store.
Now on this graph here you'll notice that we don't really
have a solution for gameplay servers.
And the only thing I can say about that is stay tuned.
So the rundown.
So grab your coffee, grab your laptop, grab your phone.
Let's talk about what we're going to do today.
So we're going to talk about content distribution,
databases, proper practices there.
We're going to talk about login and authentication.
You'd actually be surprised about how many problems there
are with login and authentication that make users
drop off the radar.
Then we're going to talk about localization, rendering, web
and platform, and user metrics.
These are all important things.
And then finally, we're going to finish this off with
monetization which--
I put that at the end because we all know you're only here
to talk about monetization.
The rest of this stuff is icing on the cake.
So let's talk about serving content.
Start at the beginning.
So let's say all of your data is served
from some server somewhere.
Your data is usually uploaded to the web.
That web creates the information.
It stores it.
It scales it.
It replicates it.
You can see here are some of standard items do you
typically see on your server--
HTML, JavaScript, images, JSON files, ZIPs,
sound files, and whatnot.
What happens is that the client--
the user machine effectively--
sits around and it will generally do a page load or
resource or load or from JavaScript an XHttpRequest you
can Google that and figure out what the
proper syntax is there--
to fetch this data from the internet or
the server in general.
The cool thing is that Chrome--
again, we're going to focus on that--
Chrome will automatically cache anything given through
this load for you.
So if you actually do a page load, Chrome will go ahead and
grab the HTML and JavaScript and JPGs and
JSON data for you.
If you need to grab your ZIP files or your OGG datas,
you're probably going through some resource load or some
XHttpRequest. And when you do those, Chrome will cache that
data as well.
The good news about this is that caching is automatic.
You don't have to do anything to take advantage of It.
You grab your data.
It's stored on the local disk.
When the user requests that data again, Chrome will check
the local disk first. If it's there, it'll grab it and load
it up and use it.
It it's timed out or if it detects that there's some of
the reason it needs, it'll go back to the web and grab it
from there.
This is all handled on your behalf.
The bad news about this though is that it is in fact handled
on your behalf, which means that any time during the day
the user can either delete their cache or go download 17
gigs of other data.
And that data will invalidate the cache and
push your stuff out.
So really, you have no ability to track when your data's
going to be evicted, and how that's going to relate to the
end user experience.
So the good news is modern browsers actually implement a
specific file store API.
The file store API allows you to write to a persistent
obfuscated sandbox section of the user's hard drive.
Which means that when you actually grab this data from
the web, you can actually store it somewhere so that you
control the caching, so that then you directly control the
user's experience with the caching.
And that's very important.
If you've got 14 gigabytes of data sitting around, you're
going to want to know how the user is loading it and when
they're loading so that you can optimize for their
experience.
At the end of the day, they're the guys give you the money.
You gotta make it right for them.
So let's talk about scaling.
So let's say you've made in the most amazing game ever.
It's called catfightpig.com.
Whatever.
I don't know.
And you make this game.
You put it up on the web.
And then all of a sudden, you get a million billion 100,000
people that decide to download your game and play and run it.
Well, the problem is that you've just went viral.
And if you don't have a proper scaling solution on your
server side to handle the scaling the just happened on
the client side, your viral ability is going to go through
the floor really quick.
So what happens is you need to move to something like a
distributable content distribution server.
So you need to find some way, use some service out there to
actually replicate your content across all of these
different servers so that it can be handed to the clients
as they request it.
Now there's some great services out there, for
example Google App Engine which will handle replication
as well as regional replication on your behalf.
That means that say someone in France is accessing your data,
and you've uploaded it to servers here in California.
They're still going to get fast access speeds to that
data because Google's handled all the back end of making
sure that it exists on a web server that's near that user
so they get it at the correct speeds.
The good thing is you don't have to worry about that.
If you've just rented some web server sitting somewhere in
someone's basement, you're not going to get the same ability.
So international customers or customers that may not have
direct access to that guy's basement, Bill's basement
we'll call it, are going to get a lower feature set.
So when you're serving this data, it's important to think
about what the costs associated with it are.
Hosting data has a cost. And let's talk about how it's
being used.
So today, typically developers use loose files.
They define--
and this is sort of defined as a result of the way that HTML
and JavaScript works.
You can simply go in there.
You define an image.
You define a JavaScript link.
You define these as an exact path to a resource on the web.
So what happens is the browser will effectively issue
asynchronous loads for all of these web resources.
So it'll go out to the internet.
It'll say hey, please give me this thing.
Let me know once you've received it.
Now this is fantastic.
This mean that the web browser and the client are sort of
optimizing the load process based on your behalf, sending
all this stuff out and getting them.
And of course, when it does receive it, it puts it in the
Chrome cache, which is again good for you.
Of course, we talked about the Chrome cache before and all
the benefits and downsides of that.
Now anyone who's shipped a game on the PC or on console
knows that archiving is a much, much better way to
handle this.
Because of the fact that any time you read from a resource
or a medium--
be it hard drive or CD or even the internet--
you're going to induce a lot of bandwidth incurrence.
So you're going to say give me this load.
It's going to take 10 seconds, 20 seconds, a minute, two
minutes, whatever.
You want to reduce that amount of time.
Now if you have tons and tons and tons and tons of files,
you may have to be blocking some files, loading others in
different orders.
It just gets into madness.
So what most developers do is they'll actually zip all of
this data up, or they'll archive the information.
They put all of their loose files into a single archive,
and then they download that archive.
Now once you do this though, you have to note that the
archiving of this information is not going to come through
the same paths.
What you want to be doing to make sure that this archive
comes down properly is store it in the
persistent file store.
And then you're going to want to modify your runtime code so
that it checks the persistent file store for the existence
of the zip first before actually communicating with
the rest of their system.
So this is a benefit.
This means that you are now controlling how the assets are
bundled, how they're distributed, how they get to
the user's machine, and how the user machine responds to
having them.
This gives you a lot of control.
Again, this is important because it optimizes for the
user's experience.
You can stream data in, you can stream it out, et cetera.
Now one of the bad sides of archiving is twofold.
First off is that you sort of lose the ability to just
randomly grab files from the internet.
You have to grab these larger blobs.
And these larger blobs then sometimes take more time, so
you have to make different loading screens and
asynchronous results and all these other things.
So you want to move towards something called segmenting.
Effectively, the idea behind this is it you're not going to
just create one large zip file for all of your assets.
You're going to create many smaller archives.
And these archives are going to be bundle based upon
different parameters and metrics.
For instance, you could say that your original zip file is
only five megs.
That zip file contains everything like the main menu
and the starting guide so the user can load your game and
see a spinning character and do their character creation.
Meanwhile, it's actually streaming in the other
archive behind it.
So this allows your user to get into the game immediately,
and then spend time doing something instead of waiting
for the loading bar to get all their data in there.
Another reason to segment your assets is due to monetization.
So let's say that your particular game where you sell
chunks of game content in one gigabyte bursts.
So chapter five, chapter six, chapter seven.
And there's a large demand for each one of those from a
monetization standpoint.
What you're going to want to do is you're going to want to
segment each of those in two separate monetization
categories so that you don't stream that data to the user
until they've actually paid you for it.
Again, hosting and streaming cost something.
So you want to optimize the content that you're streaming
to the user based upon whether or not they've paid you.
You don't want to have a 15 gigabyte game, stream it all
of the user, and only have 2% of those
users actually convert.
That means you're going to be wasting a lot of money.
It doesn't make sense.
With this though, you're going to need some sort of manifest
file because the final cool thing about archiving and some
sort of segmented set up is you actually need a way to
define what zips are invalid and not invalid from the
persistent file store.
So the user will go grab a zip from the net, they'll store it
to the local persistent store, and then you go upload a new
zip because you've made balance changes or whatnot.
Now the user needs the ability to query some sort of file
that says whether or not that zip is out of date.
So that way, they say hey, this is out of date.
We need to go fetch it from the server and update our
local cache view.
So basically you want to segment all of your stuff
based upon game resources, monetization resources, and
then create some sort of manifest file that allows the
client machine to keep up with that data.
Now let's talk about Google App Engine specifically.
Let's just focus on that for a minute and say that that's
where you're hosting your concept.
So you can store your segmented
binary archives here.
That's fantastic.
This allows you to carefully control your server costs.
Again, this is a metric that matters to you because how
you're serving your data directly relates to how much
it costs you to serve that data.
Google App Engine allows you to store any asset that's
under 32 megabytes HTML style or sort of direct store.
Which means you just upload it and it's no big deal and it's
stored just like any other file.
It's sort of seamless integration for you.
You can read the documentation on how exactly that works.
For anything above 32 megabytes though, you have to
store it from a different file store and that's
the blobstore API.
Now it's still hosted on Google App Engine, but you
have to use a separate API to access it.
And this is again really important.
This sort of forces you to optimize how you're getting
your data, because there may be tiered costs available.
There may be specifics streaming solutions you need
to be aware of.
This is important stuff.
Also for that, you need to set the browser
cache expiration date.
So any of this binary data that's served from Google App
Engine, when it hits the client, the client's
effectively going to say here's how long
this data is valid.
Here's how long I think you should keep this around.
So the cache then has this large amazing metric that says
hey, all of these items in the cache, they've expired, they
can be expelled to bring new data in.
You can actually set the browser time for that file.
And if you click that link there, you can actually set it
to be some larger time.
I think the default is actually 10 minutes.
So that means for a user playing your game, let's say
there's 56 hours of game play.
If you don't set this particular header for the data
that's served to your client, that means that the client,
the browser, Chrome, the cache, will say that after 10
minutes, all that data can be invalidated and flushed to
something else.
Now if you're using the persistent file store, this
shouldn't necessarily be a big deal.
But in some cases it's still smart to set
that expiration date.
This brings us to a public service announcement.
Do not use the existence of data on the user's machine to
represent that the user has purchased that data.
So anyone in here who has handled any sort of problems
with piracy for video games knows this to be true.
Effectively, if a user purchases your item and then
refunds that purchase and the item's data has been streamed
to them and stored on disk and the user who loads your game
up, what happens?
If your code actually goes through and says, oh the sword
of Antioch exists on your desk.
You must have access to it.
Then the user could effectively buy all the items,
get them all downloaded, return all the items. You
don't get any money, but they get all the free stuff.
That's not good.
You want to make sure that you don't do it that way.
So public service announcement.
Do not use the existence of the data on the disc that
you've streamed to the user to represent the fact that the
user has bought the item.
Just don't do it.
It's madness.
Let's talk about databases.
This is again correlated databases, so
not like binary databases.
So App Engine has relational databases using
the GQL query language.
And if you click on the GQL link, it'll take you to a nice
amazing page that talks about the syntax, how to use it, how
not to use it.
It also gives you great in-depth examples about how to
get started using it and all these amazing things.
As far as app Engine is concerned, it really gives you
the trifecta of hosting data, hosting content, and also
giving you database.
The cool thing too is it auto-scales to handle demand.
This is the most important thing.
Let's say that you go a year and a half of having your app
online and nobody downloads it.
Then one day, you add in a cat with a really cool pink wizard
hat, then all of a sudden the entire internet has
to have your game.
When the entire internet starts knocking on your door,
the servers for Google App Engine will scale up
dynamically to handle that balance load.
You don't have to do anything.
If you've rented your own servers, this
is a massive problem.
All of a sudden you need to employ five IT guys and six
more server racks and buy all this hardware that then in
another month you may not need.
So you've invested a lot of sunk cost into something
that's pretty much seasonal.
The fact that App Engine handles
that for you is amazing.
Of course, the cost is based upon performance here.
So this means that as more users are using your service
and monetizing through you service, of course the cost
goes up a little bit.
The goal is to make sure that the ratio between these two is
always set in your favor.
And to do this, to make sure of this the first thing you
need to do is make sure the set the
cache-time for any GET responses.
And now a GET response on the internet for those of you
haven't used it, you get effectively a
POST and GET reset--
a POST and GET message is defined as either querying
data from some database or server or page versus setting
data to that server or database or page.
Now again, a GET effectively is a pull request.
So what's occurring here is that you're pulling
information from Google App Engine.
And what's happening is when you requests that data, Google
App Engine will go find that data, say OK,
this result has occurred.
I'm going to cache this away in case this user or a next
user or someone else goes and tries to fetch the same data.
If it finds it in its cache automatically, it'll go ahead
and return that data for you.
But you need to set the cache-time for that.
Otherwise much like the Chrome cache in the client's machine,
it'll evict it based upon duration.
So here's how to do that in Python.
Again, there's great links on the intertubes about how to do
this directly.
So we create a date object.
We set it to a specific age way, way in the future.
And then in our response packet that Google App Engine
provides to the client, we actually set the maximum age
for that data to some large metric.
So this allows the clients to then cache it on
their side, as well.
Which is good because then the client says hey, I'm going to
query the same data.
I don't even have to go to the internet.
I just grab it locally.
This brings us to the next point is using Memcache which
is on the server side of that.
Click the link, get more information.
Memcache is effectively intended for fast access to
cache results of queries.
Now again, you've got 100 million users all hitting your
Google App Engine instance at one time, all querying data,
all grabbing it from different directions.
It would be great if you were able to analyze that and
figure out what the most common things are and then
optimize different sets for that.
Now if you don't have that ability your or the stuff is
just too far across the map, then you want to use something
called Memcache.
And Memcache works like this.
Effectively you test whether or not the Memcache has some
specific data.
If it has the data, then you return that data.
So let's say the user says I want to find out how many
people are playing the game right now in Argentina.
And you store that in into Memcache.
It's already there, so you return the number of people
playing the game in Argentina.
If it doesn't exist-- if the data hasn't
been stored in Memcache--
then you actually get the data, find out how many people
are playing, store it into Memcache, and then return it
to the user.
So this means the next person that comes through the says
hey, I'm in Argentina, find the number of people playing,
it'll return the proper result.
And of course, there's a lot of amazing things behind the
scenes about what happens when that data changes without them
knowing about it, things like that.
We aren't going to get into that.
Click the link, learn more.
Expand your horizons.
Learn some new stuff today.
So most of your clients will again as we talked about have
some sort of networked component.
You're going to want to communicate with other players
is the basic multiplayer thing.
You're on the internet.
You want to play with other people.
Social is more fun.
So there's one technique to do.
This is called polling, which is effectively where each
client that's connected to a service will actually poll the
server and say hey, has anything changed.
If something's changed, the server pushes the data to the
client through the request process, and
everyone moves forward.
The problem with this is that for low latency games, games
where things only update once every 5 minutes, 10 minutes,
30 seconds, something like that, you're going to spend a
lot of time asking the server for things effectively
clogging your pipe, upping your cost,
et cetera, et cetera.
And this can be avoided by using something on Google App
Engine called the Channel API.
The Channel API effectively allows your client to
communicate the server and open a pipe that stays open.
So then the server when data does change, like the number
of players in Argentina, that when that does change, the
server tells the client hey, this data has changed.
I need you to update on your side.
This allows your client your server to communicate only
when they have to.
And this is a good, good thing.
Because this means that your overhead for communication is
low, the amount of cost spent on transfer and
bandwidth is low.
This Is a fantastic thing.
One of the downsides with the Channel API right now is that
it can only communicate with JavaScript.
So this is really highlighted and designed for
web games in general.
For those of you who want to communicate with the Channel
API on Google App Engine and using C++ or some other sort
of language, it's worth noting that the Channel API is
actually based upon a protocol called XMPP.
And effectively it wraps all the communication and
handshaking up into JavaScript and provides a JavaScript
library to you.
Now if you are so inclined that you would like to use
this technology, feel free to actually deconstruct what the
JavaScript is wrapping and figure out the proper
handshakes and message passing.
That's up to you.
I have talked with some people and don't see any particular
wrappers for different languages in the pipe.
But if there's a lot of user demand, maybe
we can change that.
So email me.
I'm here to help.
Login and authentication.
This is a pain point.
So how many of you in here got wanted to play a game and then
as soon as it asks you to log in or create an account, you
just shut the game off.
That's barrier zero.
Like man, I just want to play this game.
Why do you need my data?
I don't want to give you my information.
I'm afraid what you're going to do with it or I don't want
to get spammed.
So let's talk about best practices for login and
authentication.
So everyone's seen this.
This is a standard login.
Please login.
If you don't have an account, click here to create one.
The problem is, your website may have that, but so every
other site on the internet.
Everyone says I want to create your login.
I want to create an account.
I want to hold user information, et cetera.
This is not beneficial for the user.
The user does not want to come to your page
to create an account.
That's not the reason they're there.
The reason they're there is to play your game.
And you want to reduce the barrier to that, because that
means you'll get more money.
So the goal here is rather than asking the user to create
an account, allow them to use an existing account to login.
And there's some great technologies out there that
allow this.
One of them is OpenID.
I'm going to talk about OpenID here for a second.
OpenID effectively allows you--
it's effectively a decentralized
authentication service.
So the user can effectively use multiple different logins
to access your account.
And effectively what occurs here is the user will click
their given account provider that they want--
Google, Yahoo, Blogger, something like that.
They'll click sign in.
Sign in auto-direct the user to another page handled by
those providers.
Authentication will occur on their side.
You will then get a proper result saying did they pass or
did they fail.
So you don't even have to handle all the handshaking.
All you have to do is provide the link, the information, let
them let them handle it.
This is fantastic.
This means you get ti take all of those different dialogues
and bring them down.
So the user can use something they already have to login.
Reduction of barriers.
It's what we're all about.
Now how this works with your side though is that when the
user logs in with one of these other services or through
OpenID, it's going to hand you an authentication key.
It's going to say here's the GUID for this user.
Now it's important that you do not pass this user ID around
sort of willynilly.
This is a very important concept here.
So you're going to have your relational data table here,
and you're going to receive that auth key.
The problem is you're also going to receive the auth key
probably for a number of other providers as well.
So let's say you really want to go the gamut here.
You're allow the user to use login their Google+ ID, with
their Facebook ID, with their Apple ID, and all these other
sorts of things.
You're going to have unique IDs populating this table.
So the question is there, if you're going to hand those
keys around, which one do you pass around?
Is the first one they uses?
Is it the last one they use?
It is some MD5 hash of all the things concatenated.
No.
The better way to do this is to actually
create a user game key.
So the first time the user logs into your service, if
their unique ID in some way, shape, or form doesn't exist
already in the database, go ahead and create one and
create a new user key.
You want to use that user key everywhere
else in your database.
So if you've got your primary player information, the number
of games they have, their friends list, this should all
be sorted by the game key that's unique to your game,
not the key that's unique to their authentication provider.
It's very important for you.
In addition to this, this is actually sort of an
interesting concept that people go back and forth on.
On one side you, want to reduce the barrier to the user
logging into your service because you want to make sure
to make sure that they get there, they get to play in the
game, and they get the service benefits from logging in that
you provide for them.
On the other side though, you want to create a massive
amount of security.
I think one of the constant news articles you have always
showing up on the internet is my account
got hacked by blank.
People are constantly losing their accounts.
They're constantly losing their game
progress, their servers.
And within in-app purchase being sort of where it is and
the dominating force it has in the monetization realm right
now, you want to make sure that users have a secure way
of logging into your game and keeping their content secure,
so that their sister or their brother or their angry ex
doesn't go and do something malicious.
So one way to do this is two-step security How this
works is that the user logs in with their first ID, so their
username and the password.
And they're then prompted to enter a verification code
generated by some application.
Now this application could come in many forms. It could
be a key ring that has information associated to it.
It can come from your mobile phone, et cetera, et cetera.
But the purpose is that the user has to then authenticate
with a random number that's generated on some time series.
So for you or random person to access someone else's account
that has two-step authentication set up, you not
only have to have their username, but you have to have
their password as well, and you have to have their mobile
phone and be able to get access in their mobile phone
to that program that actually provides this key.
Now this is pretty good security across the board.
The good news is for you that you can set up this service on
your web server and on your account for free and use it
for free, and provide it to your users for free.
Really there's no reason to not allow this.
Now don't force it.
Allow your users to decide if they want it or not.
But there's no reason to not do it.
In fact there's a great link here at the bottom.
If you click that, it'll take to page that has a JavaScript
implementation, a server side implementation, and actually
shows you how to put the whole thing together within like 20
minutes of work.
Now that's fantastic.
And the cool thing is they can use their existing Google
authenticator application on their iPhone or their Android
phone, so they could use your service.
It's a great thing to do.
Localization.
This is a big one.
Console developers.
Let's see your hands if you've ever ran into problems with
localization.
Everyone.
Exactly.
It's not a big deal.
Localizing matters.
And this should matter more as a web game than any sort of
console developer or any PC developer has
ever told you before.
There are big markets internationally.
We've seen some games monetize horribly in the States that
completely blew up in Europe.
Like David Hasselhoff.
No one bought this guy's musical album in America.
But for some reason, he's a pop diva in
Europe and in Germany.
I don't get.
I didn't like his music.
I listened to it.
It wasn't for me, but hey, I'm this guy.
So the fact is that if you're going actually take your game
and move it into these markets to try to capitalize on the
monetization opportunities there, the first step you need
to look at is language.
Your game has a certain amount of strings or certain amount
of text that's being prompted to the user.
You've got menus.
You got dialogues.
You've got item descriptions.
You've got cost description.
You have quest understandings.
You've got a lot of text.
If you want to move into these other markets, you need to
communicate in their language because not everyone speaks
the language that you speak.
The problem is that all the languages don't act the same.
For instance, the first problem is the languages all
read in different directions.
So let's say you've created this amazing storyline and
your UI designer came through created amazing UI for this
for the storyline to present it to the user and display it.
The problem is when you put it in Japanese, Japanese reads
vertically instead of horizontally.
So what happens now?
How does the UI stand up to that?
This sort of forces you to go back and change your UI layout
based upon the languages that are available.
In addition that, long words and phrases are a problem.
How the language is translated in your language, English for
example, to other languages, German, may
be completely different.
Like what's 3 words or 15 characters in English maybe 37
characters in German.
A great story about this is in-app purchase.
So the in-app purchase guys had originally created some
specific UI and specific dialogue, and had a specific
window size and had it all scaling and working perfectly.
But when that data was auto-translated to German,
what happened was the text actually pushed the buy button
off the side of the screen and you
couldn't resize the window.
So what happened was if you were trying to buy this item
in Germany, you couldn't buy it because the buy button was
completely gone.
This is the type of problems that you need to be thinking
about with your UI.
Of course, word wrap and everything was implemented
very quickly.
And then everyone was able to purchase, but the problem is
that this is real.
So how you do then.
So let's say OK, cool.
I want to localize my product.
The first step is detecting the user's language.
In JavaScript, you can use a nifty little function here
navigator.language.
Simple and easy to use.
The problem is that this actually isn't
changeable by the user.
So if the user actually goes into their browser and checks
the language in their browser, it doesn't change the result
of navigator.language.
This is actually a common problem that
everyone deals with.
What it does change, though, is it changes is the
HTTPaccept header when you're doing a GET or SET response.
So there's kind of a work around in hack, but I'm giving
a fast pass here so you don't have to deal
with the same problem.
So what you can do it this is knowing that the GET and SET
requests actually have the real language that the user
has set in their browser, you could actually poll a server
and get the result back and that'll have the
real language in it.
And here's how to do that.
There's a great website up there called
ajaxhttpheaders.appspot.com.
He's a very generous guy.
You can click the link at the bottom to go his post about
how he did all this stuff.
But effectively, you send off an AJAX
request to that location.
When you get your response, you query for a specific
Accept-Language.
And that's going to have the two digit code that actually
responds to the user's language.
Once you get that two digit code, you can then pass that
off your localization process and that will then define
which table you pull strings from.
The link at the bottom by the way has the source code for
the client side JavaScript function which you're looking
at here, as well as the server side code which is posted at
ajaxhttpheaders.
And I think he actually has a jQuery module as well.
So if you're trying to detect the language,
navigator.language works well, but this is actually sort of
the definitive thing.
Now the cool thing is-- and I feel this isn't really a best
practice, but this is important to point this out as
a tool chain process--
is that there are some tools to actually do
translation for you.
Most the time for game development, you usually get
all your strings in a file, outsource that to some firm in
Dublin usually is the case, and they'll charge you some
money and they'll give you back everything translated.
This doesn't always work.
Like sometimes you need to iterate on things, you don't
have time to translating or you don't
have a budget to translate.
There's some really cool tools that Google provides that
allows you to fix that.
The first is Google Translate.
So you can type in some stuff on one side, convert it the
other language on the other.
This is easy.
Copy, paste, you're good to go.
This doesn't work in situations though where
there's sort of live chat.
So let's say you've got a chat window up and I'm
communicating to someone who's speaking Spanish.
I need the ability to take my string and
convert it to their language.
Google Translate provides an API where you can put in your
language, put in what language you want it to go to, and
it'll return a packet with the proper translated strength.
In addition to that, it also provide a detect function.
So let's say the user is using a language and they haven't
specified it properly or whatnot.
Let the string get passed into Google Translate API, and
Google Translate will sort of parse through its massive
database of languages and words and return to you what
the language is.
Now again this is fantastic because you can use it sort of
at a tool level.
You could actually say write a little application that will
take all of your table strings written in your source
language like English, and for each string send off a query
to Google Translate and it'll give you
the translated string.
This works, although it takes some time.
So the better solution is actually to use an amazing
product called Translator Toolkit.
And Translator Toolkit is free for anyone to go and use.
Translator Toolkit allows you to upload a file--
XML, HTML, CSV, things like that-- and it'll actually
translate the whole thing for you just in one shot go.
So this is actually the tool you want to be using.
If you've got all of your strings segmented into
different sections, you can take each one of those files,
upload it to Google Translator, the Toolkit, hit
the button, and it'll push it out.
Now the cool thing is here is it actually allows you to step
through each one of the items that it's translated.
So you can actually see Hello, world see example blahblahblah
actually gets translated into this over here.
I hope the Native Client team does not mind me using their
page for example purposes.
So definitely use these tools if you're doing translation.
Let's talk about display and rendering.
So we've got a game.
We've talked about the server side aspects.
Let's talk about the client side.
The client side.
You're going to display your game.
Let's talk about what that means.
First off is this is a modern web.
We have to face it.
If we really want people to take gaming on the web
seriously and take all of the skill sets that we've
acclimated over the past 30 years of gaming on PC and
gaming on consoles, we have to talk about the GPU being used
in a web page and in a web browser.
And the good news is, we've got this.
We've got technologies like OpenGL in Native Client.
We've got technologies like WebGL, and we also have
technologies like Hardware-Accelerated Canvas.
We'll talk about that in a minute.
The important thing here those is I'm sure you've all heard
the news that with these technologies, you need to be
aware that some of the driver implementations may not be
secure, may not be safe.
This means that potential malicious users can write some
sort of code, a GPU-specific code, that when a user loads
it on the local machine does something nasty and gnarly.
For the most part, the teams work in the modern browsers to
fix these, update these, and make that sure these patches
are proper.
For the stuff they just can't fix, there's actually a
presented blacklisted drivers.
They say that if you try to load this content and you have
this driver on your machine, nah.
Not going to happen.
Why?
Because the security risk is more important than the
content viewing.
Now this is really important.
Think of you.
You are a developer and you put content on the web, and
you put a game on the web and the user goes to that site and
potentially is available for some sort of hack or
something going on.
That's not good, because they're going to come back and
say that it's your problem, not the web browser's problem
or the malicious person's problem or
someone else's problem.
So you like this.
You want to make sure that you can detect if they have a
blacklisted driver, respond to it early.
Here's I do that.
It's she really simple.
You take this little snippet of code.
Effectively all you're doing in JavaScript is you get the
canvas element and you try to create a WebGL context.
If the WebGL context fails, chances are the user either
has inadequate resources to run the game, which you
wouldn't want them to be able to run it anyway, or they have
a blacklisted driver.
In either case, running this little script right here
before you start caching or loading or doing any
processing is of vital importance because you can
then tell the user hey, something's wrong.
Go update, go do something.
Does need to.
In addition here-- you can actually see something else--
is that if this passes, you can actually do other tests
here like does your hardware support this max texture size?
Which is also really important.
You have to scale to make sure that the hardware which is not
homogeneous can play well with your game.
Now most internet users aren't going to like this because
this means that I have to create different versions of
my product for different tiers of performance.
But in reality, PC developers have had
this problem for years.
This is nothing new to us.
We've been dealing with this a long time.
And the same problems and the same solutions
have existed there.
This is a well-documented thing about how to deal with.
So I suggest if this is an issue that you're worried
about, definitely read up on the documentation.
Now the next thing you need to be concerned
with is sandbox rendering.
Now you have to understand that again with these drivers
and the security issues involved that we don't
actually allow the script code that's being executed to go
right to the driver and right to the
graphics processing unit.
This could expose a whole slew of problems. So instead what
occurs is we actually put all of the processing and direct
correlation with the APIs in a separate process that's
running somewhere else.
So you got your main Chrome process and you've got a
rendering process.
All of the data is then pushed into an RPC buffer.
That RPC buffer will get the commands and then some time in
the future, push them to the render process.
And what this means you're sort of creating a frame of
buffering and a frame of latency in getting all this
through the pipe.
To dig into this a little bit deeper, you have to be wary
and the reason this is the problem is you're got to get
something called a render stall.
Basically what happens is you're going to do some work.
You're going to draw some objects, you're going to
update some vertex buffers, and you're going to fill in
your entire RPC buffer.
So that when you go to try and do something that pushes in
that RPC buffer, it's going to block.
It's going to say I've got no more room.
What's going to happen there is even though you're not done
with your frame and you have all this other stuff to do,
it's going to insert a sync FLUSH command,
effectively a glFinish.
What a sync FLUSH command's going to do is it's going to
wait for that entire buffer to be consumed
by the render process.
And it's going to block your calling application until that
work is done.
So what's going to happen is you will be
chugging along fine.
You'll see that the timing observation for draw objects
and update VBs is pretty much consistent.
And then all of a sudden, you're going to hit 30
milliseconds draw object frame.
And this is not ideal, because you're going to
try to drill down.
You're going to be like why is this one draw object taking so
long, and if I move it around it's not in the same spot and
oh, my God.
What's happening?
What's happening is it has nothing to do with the way
you're calling the API.
It has everything to do with what API's doing on the back
end that you have very little control over.
But you do have some control.
And This is really important.
Swap buffers at the end of a frame for rendering in WebGL
or OpenGL ES 2.0 in Native Client will actually kick off
a glFinish call on your behalf.
And what this does is it actually force the RPC buffer
to get flushed for you.
Now the intent here is can we insert flushes ahead of time
to make sure that we never hit one of these sync flushes.
So to do this, effectively what you have to do is you
have to figure out the optimal places to insert a glFlush
command in your rendering process.
This will allow you to do some drawing, flush the buffer, do
some more drawing, and flush the buffer again.
This will keep you from ever hitting the sync flush if you
do it right.
You may hit it every now again in periods of high bandwidth
or high amount of processing churn.
But the goal is to analyze your system, figure out where
the heavy points are, in insert these manual flushes in
strategic positions to reduce and keep that pipeline moving
as efficient as possible.
Think of it like a factory.
Like you're trying to build cars, you're trying to keep
everything going through smooth so that there's no
pipeline bottlenecks.
So canvas rendering.
I talked about this.
The latest versions of Chrome actually have GPU accelerated
campus rendering.
I believe this is unveiled as default in Chrome 17.
It might be Chrome 16.
Fact check me on that.
But what this means is that you as a developer can
actually just use the same canvas commands--
draw, tesselate, do all these other things--
and behind the scenes Chrome will actually go through and
use the GPU on your behalf.
This is amazing because this means that all of the concept
and all of the nuances of managing texture state,
setting data, and doing all this other stuff is handled
behind the scenes on your behalf.
That's great.
That means you can just make your game and focus
on making your game.
It's definitely hand-free usage.
It's behind the scenes.
And the user most importantly can toggle this with a flag.
So if by some chance they've got a beefier CPU than they do
a GPU, the user can then go in toggle, turnoff hardware
accelerated canvas, and go back to the CPU path.
We see this commonly in notebooks or netbooks that may
have like six cores sitting around but still have an
integrated graphics chip that can't run Shader Model 1.1.
Cool.
So you've got all this stuff working forward.
You've got this game environment.
You've got it all set up.
Let's talk about the platform environment.
So now that you're doing a web game, let's talk about some
things you have to be aware of.
Users will tab away from your game.
If you're running in a browser, users consistently
tab across to check their email, during a loading
screen, or they get a chat, or Groupon pops up and says, oh
my gosh, there's an amazing cat for sale in Southern
California.
Like there's different reasons for you to tab away.
You can detect this, and this is an important thing.
You can actually listen for the visibilitychange event in
JavaScript.
And that visibilitychange event will give you a
notification when the user has tabbed away.
And you need to respond according to this so.
So for instance, you need to pause the game first off.
If the player's in a multiplayer match or doing
something important, you don't want them tabbing away to
cause a problem in the game.
You want to make sure that you pause properly and make sure
everything's kosher.
You can also reduce or turn off volume.
This depends on your game.
Some people just choose to reduce the volume so you kind
of notice that you're still playing a game
and aware of it.
If you turn off the volume, sometimes it's easy for the
user to forget they're playing.
It's also worth noting that setInterval during the time
that the user is a way will actually slow down to about a
1,000 milliseconds, so about one second pulses for anything
using setInterval.
If you're using RequestAnimationFrame, it will
stop completely.
So make sure that you're aware of these things in JavaScript.
If you're using either setInterval or
RequestAnimationFrame and the user tabs away, these things
will occur.
And you need to respond accordingly to them.
Bugs in the wild.
So users will run your product, and they will run
your product on their machine in their environment on their
particular test harnesses with their particular installation
configurations and extensions and pop-ups and everything
else that's all there.
And they're going to hit a bug sooner or later.
It's just the configuration world.
Your job is to detect this on their machine and get the
information back in an appropriate way so that you
can update your product to be a better product.
Again all going back to optimizing for the end user
experience.
So you can detect this.
So if the user has an error that's thrown, you can use
window.onerror.
Or if you're using any browser that implements v8, which is
Chrome's JavaScript virtual machine, you can actually use
Error.captureStackTrace and this will give you a full
stack trace to where the error is.
Once you detect this, you need to respond accordingly.
You need to gather information.
So it's not just enough sometimes to get your stack.
You actually need to gather other information like the
game specific or game state, the operating
system, the player ID.
Maybe this one player keeps getting crashes, and you need
to correlate that with who it is.
In addition to that, there's two really cool things here,
window.performance and chrome.tabs.captureVisibleTab.
These are excellent useful resources as well, because it
allows you give a bigger picture.
For instance, what if the reason that the game keeps
crashing on this user's machine is because they have
some extension installed that allows them to arbitrarily
break point JavaScript or make everything in a floating point
integer or something.
Who knows?
Querying these things allow you to get better
visibility in that.
Once you get the information-- so you detected it, you've
gathered your information, and now you need to
get it back to you.
And there's two primary ways to do this.
The first of course is you just spin up a thread and
shoot off an email to yourself,
which is fine and dandy.
Of course, you have to worry about what that means to send
an email from the user's account and
all this other stuff.
You can also upload it.
And there's two nice little software solutions called
jsErrLog and jdrop.
Click the links.
Check those out.
Basically those allow you to sort of upload some sort of
JSONized string data to a server database somewhere, and
then you can come back at your convenience and scrub through
the number of crashes for that day, rank them, implement them
into your bug system, and respond accordingly.
Now here's an interesting point.
And a lot of people don't--
they sort of understand this, but it's a
nuance that they miss--
is that you make sure that your error
reporting is stable.
The most annoying thing I've ever had to debug is my
debugging code.
Like my I'm finding a crash that's being caused by
something that I can't use because my code that's trying
to detect the crash is actually the
thing causing the crash.
Like it's a sort of Inception, [UNINTELLIGIBLE]
all the way down.
So make sure your error reporting is stable.
When you do detect a user, it's sort of best practices to
tell the user that you've detected this and display it
to the user in some very friendly methodology.
Make sure that you communicate that it's not their fault.
Nothing's more annoying than a game that tells me that I'm
playing it wrong.
Your users don't like that.
Definitely avoid technical language.
When Chrome crashes, what do get?
You get a little funny icon that kind of
brightens your day.
And it says hey, something's crashed.
Sometimes it says ah, snap or he's dead, Jim.
It makes humor.
It makes light of the fact that your data is somehow no
longer there but don't worry.
Grass will still grow, babies will still laugh, and we will
recover your data at a point in the future.
Once you display it, make sure that you
respect your user's privacy.
Ensure that the user knows what you're gathering, what
you're returning as far as your data's concern.
And allow the user to opt-out.
This is just sort of a nice thing to do.
Allow the user to say you what, I know
what the problem was.
I was trying to hack you system.
Please don't use this information.
Or hey, I know what the problem is.
It's this other plug-in.
Don't send the information around.
Allow them to opt-out.
Once you've got your JavaScript data, it's
important to minify and obfuscate it.
This is actually really important.
So your JavaScript code will actually take up a large chunk
of your memory footprint.
And you want to reduce sort of duplicate characters and white
spaces and other things so that it's transferred to the
user in the most efficient methodology possible for
JavaScript.
The closure compile offers this as well as obfuscation.
So this will actually allow your code to be obfuscated to
a level that a standard human being just taking a look at
the code won't be able to actually detect what's going
on inside of it.
They'll need to spend some time
deobfuscating it to get there.
plovr, I think is the proper way to do this is sort of a
front end to the closure compiler.
Definitely take a look at that.
And HTML5 boiler plate also contains some really amazing
tools for minimization and obfuscation that you should be
looking at if you're writing content in JavaScript that is
performance and size heavy.
User metrics.
Now we're getting into the cool stuff.
So you should know who your players are and you should be
using Google Analytics to do this.
Now Google Analytics as I've said before is traditionally a
web service that allows you to track who's visiting your
site, where they're coming from, what pages get the most
traffic, et cetera, et cetera, et cetera.
But you can also kickoff custom
events, and this is amazing.
This means that from JavaScript, you can kick off
an event that's attached to something going on your game.
So you can say every time a user, any user finishes level
two I'm going to report that to Google Analytics and keep
that data around.
Now I don't have a slide for it, but the tools for Google
Analytics are actually really amazing.
They're kind of from the future in terms of ability to
analyze data and look at it for specific spikes and
reasons that you need.
So if you save things like finish level 2, finish the
game, how many users chatted, how many users joined a group,
how many users kicked a chicken.
This is important information.
If you put chickens and you allow a user to kick a
chicken, you should probably figure out how many users are
actually doing that thing so you can figure out whether or
not you need to make it a quest or an upgrade.
Like hey, you've kicked a million chickens.
Halo 3 did a great job of this.
And this is actually one of the first documented sort of
in the wild publications.
What you're looking at here is a heat map.
What happened was the designers of Halo 3 during the
early testing days started logging every
place that a user died.
So any time a player died, they put that point in space.
And then they would generate these heat maps of specific
multiplayer maps every day.
And what you see here is in the red, that's where all the
deaths occur.
You can see in the blue, not so many deaths.
Now you can take this information and then
correlate, hey, see these red areas?
That's where all the action is.
Why is all the action there?
Is there some sort of power up, or is it a choke point, or
is it a strategic interest You can then take this information
and go back to redesign how your game works
based upon this data.
And you can do this through Google Analytics kicking off
these custom events.
So the high points.
What do you want to track?
So let's say cool.
You buy into it.
You're going to use it.
What are you going to track?
You should use Google Analytics for the number of
daily, weekly, and monthly users that are actually using
your product.
You should track the number of players coming from what site.
You should know whether or not you're getting more players
from Rock, Paper, Shotgun versus Kotaku, and know how to
respond accordingly to that.
You should definitely know how many players actually click
the buy button.
That's super important.
Because you should also know how many players actually
complete the buy button.
This is an important metric.
This tells you how many people are dropping off inside of the
monetization process.
How many do were willing to buy the product versus how
many people actually do.
Now for some things, Google Analytics doesn't work well.
And in these cases, you should probably propagate data back
to your own server.
For some in-game events, this is probably the
best way to do it.
Like number of crashes, bugs, specific errors, or anything
that may need some specific processing that Google
Analytics isn't best suited for.
AB testing.
This is a great thing you should all be doing.
Now web developers know this.
They know that there is a secret sauce to a better game.
And it comes from the fact that there's a direct
disconnect between game developers and users.
This is just the way of things.
As a game developer, I super think that this amazing quest
where I take a chicken and wear it as a hat for three
hours is the most amazing, visceral experience that every
player will ever go through ever.
In reality, only two people play this game, me and my mom
because I told her to play it and give me feedback.
Now why I'm getting feedback from my mom on a quest, that's
a different talk.
We'll talk about that later.
But the important thing is that in order to connect with
my user which is the person playing the game, I need a
different way to get that information.
AB testing is the process of issuing a separate build to a
percentage of your users in such a way that you can then
track the results to determine what your proper outcome is.
So for instance, the most simple version of this is
currently our buy button when you want to
buy an item is red.
What if we change it to be blue?
Does that have any effect on the amount of people that
monetize our game?
Let's find out.
Let's make a build with a blue button, issue it to 50% of the
people that log in today.
50% of people keep the red button.
Did we see any change?
If not, if so what does that mean to you?
This allows you to make changes that
optimize your outcome.
Because it's not just as simple as what kind of button
color it is.
But it's also based upon region.
Like do people in Germany buy the same way
as people in America?
Could I change my layout to optimize
for that region better?
You can actually create multiple versions of your game
and your front end effectively based upon region.
Like maybe in Japan, your game needs more happy happy fun
time music.
You don't know that until you issue AB testing to get to
that information.
All right.
The thing you've all been waiting for-- monetization.
Our deep dark scientists here at Google have taken a look at
monetization and what's been working for games.
And in an anonymous fashion, we're going to sort of report
some of the things that you should be looking at if you're
developing web games today.
So the first off is that monetization is a dark art.
What works for one game does not work for every game.
And you need to make sure you understand that first off.
You can't just grab a game and say I'm going to monetize like
they're monetizing.
It has a lot to do with how integrated it is with your
game, with your product, and with your environment.
So with that being said, you need to make sure that you're
holding the user's hand.
For instance, we go through this entire process of
teaching the user how to play the game, and we never teach
them how to purchase an item.
We've actually seen that the games that include purchase in
the tutorial monetize on average about 30% better than
those who don't.
That's amazing.
Just showing the user how to--
give them some free credits.
Let them buy something during the tutorial to show them the
store so that they're not afraid of it.
Show them what the benefits are.
Allow them to browse around.
We've got some other cool statistics that show the user
who do buy during that process are tons more sticky.
We'll share that information at a different time.
Most importantly with this is why they should purchase.
Getting them early in this process and showing them how
they should purchase allows them to also
see why it's available.
Most of the time users play a game, they don't have any
bearing or understanding about what's available and why they
care about it.
For instance, I never knew there was a sword of Antioch
that was worth 50-50 power.
It allowed me to assault chickens from here until the
Great Gates of Nagral.
But now that I do know, I want to buy that sword.
Getting them in there and showing them
that item is important.
This is a great graph.
This is an amazing graph.
And if you've been reading anything on monetization in
games recently, you should already know
the outcome of this.
What this shows is three line points.
This is from a particular product that launched a full
version that you could buy up front.
So you pay $5.99 and you get the full version, and a free
version at the same time.
So they said pay $5 or don't pay $5.
I don't really care.
It also provided in-app purchase so that you can
actually purchase items in the game.
So you're looking at this time line here.
In the blue on the bottom, you're actually looking at the
full purchase.
So this is just the amount of money that came from buying
the game, just without anything else.
Just buy the game up front.
You can see that in the blue.
The green is the amount of money made from the people who
bought the full game-- so upfront price--
as well as in-app purchase.
So they bought the game, they spent $5, and then they spend
an additional $10 or $11 buying upgraded items.
The pink or the orange depending on what your monitor
color is and how this gets compressed on YouTube is
actually the total input from the free app
just for in-app purchase.
Which means these people didn't buy up front version.
They just spent all of their money in the game actually
buying items. It's through the roof in a
couple different places.
But in general, it just over-monetizes everything.
And the reason for this can be twofold.
Number one, it could be that the number of users who got in
the door to buy the item were much larger.
So let's say that the free version to in-app purchase had
3x to 4x the number of users.
Or the users just felt more generous because they didn't
already pay $5 to get the application.
But the point of this graph-- and we're seeing other results
show this--
is that making your game free to play, some content free to
play with some form of in-app purchase for content expansion
is definitely the way to go.
Now once you do this though, you're going to ask yourself
what kind of virtual goods should I be selling?
If we're going to embrace this in-app purchase thing, what
should I be doing?
There's a great graph by Flurry that's online.
Go grab it.
Take a look at it.
Which actually shows the breakdown of durable goods and
who's buying what.
So first off, we notice that personalization items through
in-app purchase are only about 2%.
So only about 2% of transactions in this graph are
for hey, I would like to buy that shiny chicken hat that
clucks any time I get next to someone else who
has a chicken hat.
Only about 2%.
About 30% are durable items. These are effectively items
that you buy and then keep the rest of your gaming existence.
So the sword of Antioch that I then keep until I'm level 90.
Only about 30% buy that.
Where the big money is actually in consumable goods.
And basically consumable goods are goods that
have a limited lifespan.
So pay $0.99 to get a speed boost for the next hour.
Or also included in this item is virtual currency.
So pay $5 to get 200 coins.
And then the coins can then be used to purchase other items
in the game.
Virtual currency is considered a consumable good.
And as you can see, that whole section of consumables--
so limited lifetime objects--
are actually dominating how people are buying nowadays.
So let's talk about virtual currency for a second.
So if you have virtual currency, the numbers actually
show that you should have a multiple currency system.
Not a multiple virtual currency, but a multiple
currency system.
So you should be able to allow your users to effectively buy
premium currency from in-app purchases, and then also have
some sort of grind currency from game play.
So I play this level, I kick a chicken, the
chicken drops one bronze.
Cool.
I got a bronze.
But then I can go and buy for $5 100 gold.
And really the metric here this starts getting cool is
that you allow trading between currencies.
So I can turn my 100 gold into 5,000 copper or vice-versa.
And this creates this really interesting dynamic that the
user feels like they're smart because they feel like they're
in control.
They can either spend $5 to get the item or they can go
grind for five hours to get the same item and the same
cost and then translate between the two to buy more
items. This is really cool.
Empowering your user and letting them know that they're
in control and they feel smart is the best way to monetize
and keep players coming back.
It also enables instant gratification.
So if I go to buy an item and I don't have enough virtual
currency sitting around, you should probably allow the user
to buy that item without virtual currency.
Nothing's more annoying than saying I want to buy this
sword, but before I can actually pay you for it, I
have to go buy 100 coin points and then go
back and buy the sword.
No.
What you should say is hey, you don't have enough credits
available to you.
Would you like to buy this sword for face value at $7.99.
And this is also a great point that once you present this to
the user, you actually have a chance to upsell.
You've now got their attention.
Hey, you don't have enough points to buy this.
You can either buy it for $7.99 or what you could do is
buy 1,000 credits and we'll give you the
sword for 50% off.
That's an interesting correlation.
You now have the user's attention.
It's tied to an object they want, and they see value in a
different proposition.
That's really cool and you should take advantage of that.
You should also take advantage of simplified purchase flow.
So when the user wants to buy your item, you should make it
as easy as possible for them to do so.
So leverage in-app payment APIs.
Rather than directing the user to some other site that they
then have to go through that site and get through that
purchase flow in and then get an item and then come back and
enter a key code, that just doesn't work.
You should be using in-app APIs.
And some of these things have great features.
So for example, you get access to millions of credit cards
for these APIs.
You have powerful fraud engines that
exist on the back side.
Plug and play purchase flow through
JavaScript and whatnot.
And you get to play in a few clicks.
And Google Wallet provides all of these things.
So with Google Wallet, you can actually issue JavaScript
commands to a server, allow the user to buy items. You get
access to anyone who's got availability of a credit card
existing in Google Wallet.
We've already got fraud engines set up, purchase flow,
and all this other stuff.
Use Google Wallet.
You should definitely leverage this as opposed to forcing the
user to go through some third party site.
So let's talk about the high points here or the takeaways
or take outs.
So first off, bundle your assets into segment archives.
Really important.
Set your proper caching times for both data and query stuff
on Google App Engine.
Provide a single login for your users so that they don't
have to create multiple sessions or multiple users.
Localize your content properly.
Super important there.
Be able to detect your GPU blacklisted and performance
problems and alert the user early.
Nothing's more annoying than a user spending an hour trying
to get a game to run to finally be told through
frustration that they can't play the application.
Make sure that you detect and fetch bugs in a wild.
You need to be able to know what's going on in the
universe that you can fix it and create a better
experience.
You should also make sure that when the user tabs away that
you detect that and respond properly.
You should track the data of your users so you can optimize
your game for their experience.
And you should choose a monetization metric
that fits your game.
It's probably one of the most important points there.
And if you do, once you have chosen that monetization
method if you do you choose a path that focuses on
consumable goods, make sure you focus on consumable and
durable because that's where the largest portion of the pie
is for in-app purchase.
And then finally, for in-app purchase, definitely use the
Google Wallet API.
It's available.
It's usable.
It's quick to get started and it's amazing what kind of
power it gives you.
So in general, it takes lots of work to develop a game that
has nothing to do with your game.
A lot of the things we talked about had nothing to do with
path finding or play balancing or how to properly get a sword
to whack a demon, like nothing of that.
This is all back end stuff.
And because of that, you should be leveraging services
that make it easy.
You want to be spending time optimizing your game, not
spending time optimizing these other services.
The fantastic thing is Google's got a lot of services
that we have available for you to get started.
Definitely use them.
Definitely take advantage of them, and make sure you get
your game the best it can be.
So a big thanks to Pong Yang and Fred Sauer, as well as
Chrome Developer Relations for input on this talk.
Thank you very much.
Hit me up with any questions you have. I'm available here.