If you're not saving your user's game in the cloud,
I can guarantee you are losing players
any time they get a new device.
Because no matter how awesome your game is-- and I'm sure
it is-- nobody wants to start all over again from level one
when they reinstall it.
I'm Todd Kerpelman, and while there are many Cloud Save
solutions out there, the one I'm here to talk to you
about today is the one you get for free with Google Play
game services.
So the Play Games team has upgraded their old Cloud Save
featuring in a major way.
Heck, it's such a big update we have renamed
this feature from Cloud Save to Saved Games-- in the cloud.
And we've got a fancy new icon to go with it.
It's got an arrow in it and everything.
But it doesn't just have a new icon.
This new Saved Games feature includes a bunch
of improvements over the old Cloud Save.
The biggest of which is that you can now save blobs of data
up to 3 meg large.
That is a lot of game data.
And while you were limited to four slots earlier,
now you can have as many as you want.
Your app will now have a Saved Games badge in the Play Store
so everybody will know that they can
save their progress as they're playing.
You can also add metadata to your saved game,
like a description and a cover image.
All of which will make your saved game
show up very nicely in the Play Games app,
or within your own game.
I mean, look at that beautiful saved game.
Don't you want to click on it?
I know I do.
You know, players see this in the Play Games app.
They're reminded-- oh, yeah.
I was at this level.
I had just gotten this cool new sword.
I'm going to keep playing from where I left off.
So these features are great, but along with these new features
comes a new way of handling the saved game
data and new ways of managing conflicts.
And I always find that when dealing with this stuff--
particularly when it comes to debugging-- it's
best to have a decent understanding of how things
work under the hood instead of it being some mystery
black box.
So let's take a peek behind the scenes here.
You just might learn something.
So the first thing you need to understand
is that Saved Games are represented internally
as an object we call a snapshot.
The snapshot contains the metadata, including things
like the description of a file, a cover image, the amount
of time you've played, and a local timestamp,
as well as the giant blob of saved game data.
Saved Games are powered by Google Drive,
and when you are storing or loading
these Saved Games to the Cloud, the best way
to picture what's going on is that somewhere in the user's
Google Drive account, there is a hidden folder
just for your application where the snapshot is being stored.
And as these snapshots are being saved inside the folder,
they're given a name just like any other file.
And just like in a normal file, these names
are the primary way we identify these snapshots
to your application.
Your user, however, never sees this name.
They'll see your snapshot's description, cover image,
and maybe some other metadata, but this name
is never surfaced to them.
It's really just an identifier for your application to use.
Now you can query and examine any
of the metadata whenever you'd like.
But it's important to know that to read the saved data,
or to write to it-- even if you're writing to a brand
new snapshot-- you must open the snapshot first.
Again, I guess it's kind of analogous to accessing
local files in a language like say Python.
You could find out the last modified time of a file,
but if you want to read or write to it,
you're going to need to call open on it
to get the file handle.
Now most of this time, this open command--
it's not very exciting.
What's happening behind the scenes
is that your device is downloading all this game
data associated with your snapshot metadata
if it hasn't done so yet, and then tells your game
when it's done.
But this command does get complicated
when we handle conflicts, so stay tuned.
Now with that in mind, let's look at a simple situation
where we're going to load an existing save game
from the Cloud, make some changes,
and then save it again.
Loading or opening a saved game starts
by opening up the snapshot as identified by its name.
Now if you're only ever dealing with a single save game
that you're going to be saving and loading,
you could get away with hard coding this file
name as a constant somewhere.
Remember, these saved games are being saved in the user's drive
account in a folder that is reserved just
for your application.
So you don't need to worry about making these file names unique.
You don't have to call it com.example.myap
p.savedGame.userId.
You could just call it something as unoriginal
as like, SavedGame, or, I don't know, Dan, I guess.
In Android Java land, loading and opening a snapshot
is accomplished using the Games.Snapshots.open method.
In the iOS world, you're going to be
using the openWithFileName, conflictPolicy,
completionHandler method.
And in the C++ world, you're going to be using the snapshot
manager's open command.
The implementations do vary slightly,
but you'll notice in all cases, you
provide a name of the snapshot.
And by the end of the process, you're
left with the snapshot that has been opened
and is now available for reading or writing.
Reading the contents of a snapshot
is accomplished by calling a second command
on the open snapshot, or snapshot metadata object.
In Android land, this is readFully.
In iOS, it's readWithCompletionHandler.
And in C++, it's read or readBlocking.
Again, this is a separate command,
because while we can load the snapshot data into memory right
away, we probably don't want to have
to do this for a 3 meg chunk of game data
until we really have to.
And in all cases, you're going to get back
a byte array, some NSData, or a byte vector
that you can then pass along to your game
and process as necessary.
Now let's say that we've gone and opened your snapshot
and read in some data and passed it along to our game.
And now the player has made some progress
and wants to save their game.
To do this in Android, you're going
to need to write this game data back
onto the same snapshot you opened earlier.
You're not going to be creating a new one.
What you are going to do is create a new snapshot metadata
change object.
So let's ignore the rest of the snapshot
here and just look at this new object.
The metadata change object is basically
a list of elements in the metadata,
such as a description, time plate, or cover image,
that are different between this version of the snapshot
and what was up there before.
Basically, the saved game API will look at this change object
and kind of overlay it on top of your old metadata
to create this new metadata object.
Once you've done this, you'll call commit and close
on Android devices where you'll pass
in the old snapshot with the new data,
and the metadata change object right there.
On iOS and C++, it's slightly different in that you do create
a new game data object instead of writing over the old one,
but you're still creating a metadata change object as well.
And then all that gets sent up.
You'll take the current snapshot metadata
and call commitMethod on it.
You'll do something similar in C++.
And in both cases, you can see we have our currently
open snapshot, our metadata change
object, and our new data.
Once you've saved all this information
and committed your snapshot, it is now considered closed.
And you'll want to reopen it again
if you want to write more data to it.
So easy peasy, right?
OK, so let's look at some situations
where things can get a little funky.
What do you do if this is a brand new player, or somebody
who's never signed in to game services before,
and they try to open a saved game file that doesn't exist?
Actually, this isn't really a problem.
On Android, one of the arguments you pass to snapshots that open
is a createIfNotFound Boolean.
Set this to true, and you're all set.
You simply open up a snapshot with no data in it.
In iOS and C++, you don't even need to do that.
It'll create an empty file by default.
Then you simply treat this empty snapshot
like any other snapshot.
It's just one that has no data, and all of its metadata
is set to default values and/or null, or I guess nil on iOS.
Simply by updating this snapshot with your first set
of game data and a reasonable metadata change object,
you can get this looking like a real snapshot in no time.
So up until now, we've talked as though you're just
loading and saving the same game file over and over again.
But let's say you want to manage more than one saved game file.
How do you do that?
Well for starters, you're not going
to be hard coding your snapshot name.
You're going to need to figure out
which snapshot to save and load at run time.
And to do that, you're going to need
to give the player the ability to pick which saved game they
want to use, create a saved game, and so on.
Luckily, the UI to handle all this
is provided for you by the game services libraries.
When you want to present a user with a list of saved games,
you can call up our UI.
Now there are several settings you can tweak with this UI,
either as parameters in Android and C++,
or as delegate methods in Objective C.
These settings will let you decide how many saved games
to show in this UI, whether or not to give the user
the ability to delete a saved game, and most importantly,
whether or not the user has the ability to create a new save.
Now if I'm a user, and I click one of these existing saves,
it'll hit a callback in your game, and in that callback,
you'll get back the name of the snapshot to open.
You can now just use this name to open up the correct snapshot
just like before, and you're ready to go.
But let's say I'm a user, and I want to create a new save game.
I would click the Save New Game button,
and your app then gets a callback
that this button was clicked.
At this point, it's up to you to basically create a new snapshot
by asking to open a saved game with a name that does not
exist yet.
So how do you decide what to name it?
It's kind of up to you.
You could use a timestamp-- that's pretty good.
You could use a random number appended
to a timestamp-- I guess that's even better.
You could query the names of all the other snapshots,
examine them all, and then increment
a number for the new one.
That'd work too.
The only trick is that it needs to be
unique for this particular player.
Doesn't need to be all that memorable since,
as you just saw, you're always going
to get that file name from the UI that our library provides.
It will never be surfaced to the user.
They're never going to need to refer to it by name.
However you do it, as soon as you request a snapshot
with a file name that doesn't exist yet,
you get back an empty snapshot that you can then
use as your new game.
Keep in mind that the intended flow for all this
is like what you might see at the beginning
of a console game.
I'm at the main menu, I want to start a game,
and the game lets me pick from continuing from a previously
saved slot or starting a completely new game.
It's not really intended to let the user switch saved
game slots mid-game or duplicate their progress or anything
like that.
I mean, I suppose you could hack it to work that way,
but it's going to feel a little clunky.
OK, you've gotten this far.
Give yourself a pat on the back because you
have completed level one!
OK, stop patting because this is all good background,
but the real test comes when you just
start detecting and resolving snapshot conflicts.
And for that, you're just going to need
to watch our next video.
Hopefully this link will be live by the time
you're watching this, but if not, check back soon
because it will be.
So thanks for watching, gang.
Feel free to follow me on G+ or Twitter,
and I will see you soon for part two.
Our exciting conclusion!
Dun-dun-dun.