• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
Question by el_saq · Feb 06, 2015 at 01:29 PM · scripting problem

How to save/load with Google Play Services

Hi all! I-m trying yo implement the Google Play Services in my Android game.

https://github.com/playgameservices/play-games-plugin-for-unity

I have been able to implement ac$$anonymous$$evments without problems and are working good, but I have been some days trying to get the Saved Services working and I can´t.

The main problem is the code of the plugin page, I don´t understand how it works, only I have been able to showUI with a button but I dont pass from that point.

I need the new Saved Service, not the old one Cloud Services, thats the main problem, for the old Cloud Service are a lot of tutorial, but for the new one I can´t fine anyt$$anonymous$$ng.

I have enable Saved Games in Google Developers Console and it is publis$$anonymous$$ed more than 24 hours ago (2 days exactly).

I have enabled Saved Games in the code:

     PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder()
         // enables saving game progress.
         .EnableSavedGames()
         .Build();
     PlayGamesPlatform.InitializeInstance(config);

T$$anonymous$$s is the code provided with the plugin:

To show the default UI:

     void ShowSelectUI() {
         int maxNumToDisplay = 5;
         bool allowCreateNew = false;
         bool allowDelete = true;
 
         ISavedGameClient savedGameClient = PlayGamesPlatform.Instance.SavedGame;
         savedGameClient.ShowSelectSavedGameUI("Select saved game",
             maxNumToDisplay,
             allowCreateNew,
             allowDelete,
             OnSavedGameSelected);
     }
 
 
     public void OnSavedGameSelected (SelectUIStatus status, ISavedGameMetadata game) {
         if (status == SelectUIStatus.SavedGameSelected) {
             // handle selected game save
         } else {
             // handle cancel or error
         }
     }

To open save game:

     void OpenSavedGame(string filename) {
         ISavedGameClient savedGameClient = PlayGamesPlatform.Instance.SavedGame;
         savedGameClient.OpenWithAutomaticConflictResolution(filename, DataSource.ReadCacheOrNetwork,
             ConflictResolutionStrategy.UseLongestPlaytime, OnSavedGameOpened);
     }
 
     public void OnSavedGameOpened(SavedGameRequestStatus status, ISavedGameMetadata game) {
         if (status == SavedGameRequestStatus.Success) {
             // handle reading or writing of saved game.
         } else {
             // handle error
         }
     }

Writing save game:

     void SaveGame (ISavedGameMetadata game, byte[] savedData, TimeSpan totalPlaytime) {
         ISavedGameClient savedGameClient = PlayGamesPlatform.Instance.SavedGame;
 
         SavedGameMetadataUpdate.Builder builder = new SavedGameMetadataUpdate.Builder();
         builder = builder
             .WithUpdatedPlayedTime(totalPlaytime)
             .WithUpdatedDescription("Saved game at " + DateTime.Now());
         if (savedImage != null) {
             // T$$anonymous$$s assumes that savedImage is an instance of Texture2D
             // and that you have already called a function equivalent to
             // getScreenshot() to set savedImage
             // NOTE: see sample definition of getScreenshot() method below
             byte[] pngData = savedImage.EncodeToPNG();
             builder = builder.WithUpdatedPngCoverImage(pngData);
         }
         SavedGameMetadataUpdate updatedMetadata = builder.Build();
         savedGameClient.CommitUpdate(game, updatedMetadata, savedData, OnSavedGameWritten);
     }
 
     public void OnSavedGameWritten (SavedGameRequestStatus status, ISavedGameMetadata game) {
         if (status == SavedGameRequestStatus.Success) {
             // handle reading or writing of saved game.
         } else {
             // handle error
         }
     }
 
     public Texture2D getScreenshot() {
         // Create a 2D texture that is 1024x700 pixels from w$$anonymous$$ch the PNG will be
         // extracted
         Texture2D screenShot = new Texture2D(1024, 700);
 
         // Takes the screenshot from top left hand corner of screen and maps to top
         // left hand corner of screenShot texture
         screenShot.ReadPixels(
             new Rect(0, 0, Screen.width, (Screen.width/1024)*700), 0, 0);
         return screenShot;
     }

Reading save game:

     void LoadGameData (ISavedGameMetadata game) {
         ISavedGameClient savedGameClient = PlayGamesPlatform.Instance.SavedGame;
         savedGameClient.ReadBinaryData(game, OnSavedGameDataRead);
     }
 
     public void OnSavedGameDataRead (SavedGameRequestStatus status, byte[] data) {
         if (status == SavedGameRequestStatus.Success) {
             // handle processing the byte array data
         } else {
             // handle error
         }
     }

Thanks for your time!

Comment
MaJr85

People who like this

1 Show 6
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image el_saq · Feb 08, 2015 at 10:09 AM 0
Share

Nobody use the new Saved Games feature??

avatar image el_saq · Feb 12, 2015 at 01:19 PM 0
Share

Yeah great community.....

avatar image el_saq · Feb 12, 2015 at 06:23 PM 0
Share

I only need a simple code example of how to save and load with the official plugin, the code provided from Google is a piece of crap and incomplete code.

avatar image el_saq · Feb 21, 2015 at 11:19 AM 0
Share

This plugin is really frustrating, I´m moving to a backend server service for my games, the support and tutorials of Google are shit.

Any one hace used GameSparks or Gamedonia?

Thanks!

avatar image Michael-Glen-Montague · May 19, 2015 at 02:29 AM 0
Share

Have you looked at the samples folder? The CubicPilot sample should be a full project (minus the actual Play Games Plugin, but that's irrelevant since you can easily add it yourself) demonstrating exactly how to get Google Saved Games working.

Show more comments

3 Replies

· Add your reply
  • Sort: 
avatar image

Answer by C4nDiM4n · Aug 25, 2016 at 04:54 PM

For people who finds this thread in the future and have problems setting up GPG cloud save, like I had.

I've made the most barebones setup I can. Hopefully this will help people get started with GPG cloud save. Since this is a very light setup once it works you should tailor it to your own setup.

IMPORTANT This is Save/Load without using GPG UI. Also GPG needs to be initialized before you can use this. GPG Cloud Save needs to be turned on in Dev console and .EnableSavedGames() needs to exist in PlayGamesClientConfiguration.

This only saves a simpel string (in our case a json string with save progress) to a GPG cloud save. It overwrites the save every time so if you want multiple saves you need to implement that yourself. Comments in code should explain most.

Look at https://github.com/playgameservices/play-games-plugin-for-unity/tree/master/samples/CubicPilot/Source/Assets/CubicPilot/GameLogic for a more full setup.

This https://github.com/playgameservices/play-games-plugin-for-unity/issues/384 thread helped me a bit. Cred to iqbalmineraltown's answer. I used it in this implementation.

Also cred to @peteradamondy with the above answer. Used his his implementation for reference aswell.

 #if UNITY_ANDROID
 using UnityEngine;
 using System;
 using System.Collections.Generic;
 //gpg
 using GooglePlayGames;
 using GooglePlayGames.BasicApi;
 using GooglePlayGames.BasicApi.SavedGame;
 //for encoding
 using System.Text;
 //for extra save ui
 using UnityEngine.SocialPlatforms;
 //for text, remove
 using UnityEngine.UI;
 
 public class GPG_CloudSaveSystem {
 
     private static GPG_CloudSaveSystem _instance;
     public static GPG_CloudSaveSystem Instance{
         get{
             if (_instance == null) {
                 _instance = new GPG_CloudSaveSystem();
             }
             return _instance;
         }
     }
 
     //keep track of saving or loading during callbacks.
     private bool m_saving;
     //save name. This name will work, change it if you like.
     private static string m_saveName = "game_save_name";
     //This is the saved file. Put this in seperate class with other variables for more advanced setup. Remember to change merging, toBytes and fromBytes for more advanced setup.
     private string saveString = "";
 
     //check with GPG (or other*) if user is authenticated. *e.g. GameCenter
     private bool Authenticated {
         get {
             return Social.Active.localUser.authenticated;
         }
     }
 
     //merges loaded bytearray with old save
     private void ProcessCloudData(byte[] cloudData) {
         if (cloudData == null) {
             Debug.Log("No data saved to the cloud yet...");
             return;
         }
         Debug.Log("Decoding cloud data from bytes.");
         string progress = FromBytes(cloudData);
         Debug.Log("Merging with existing game progress.");
         MergeWith(progress);
     }
 
     //load save from cloud
     public void LoadFromCloud(){
         Debug.Log("Loading game progress from the cloud.");
         m_saving = false;
         ((PlayGamesPlatform)Social.Active).SavedGame.OpenWithAutomaticConflictResolution(
             m_saveName, //name of file.
             DataSource.ReadCacheOrNetwork,
             ConflictResolutionStrategy.UseLongestPlaytime,
             SavedGameOpened);
     }
 
     //overwrites old file or saves a new one
     public void SaveToCloud() {
         if (Authenticated) {
             Debug.Log("Saving progress to the cloud... filename: " + m_saveName);
             m_saving = true;
             //save to named file
             ((PlayGamesPlatform)Social.Active).SavedGame.OpenWithAutomaticConflictResolution(
                 m_saveName, //name of file. If save doesn't exist it will be created with this name
                 DataSource.ReadCacheOrNetwork,
                 ConflictResolutionStrategy.UseLongestPlaytime,
                 SavedGameOpened);
         } else {
             Debug.Log("Not authenticated!");
         }
     }
 
     //save is opened, either save or load it.
     private void SavedGameOpened(SavedGameRequestStatus status, ISavedGameMetadata game) {
         //check success
         if (status == SavedGameRequestStatus.Success){
             //saving
             if (m_saving){
                 //read bytes from save
                 byte[] data = ToBytes();
                 //create builder. here you can add play time, time created etc for UI.
                 SavedGameMetadataUpdate.Builder builder = new SavedGameMetadataUpdate.Builder();
                 SavedGameMetadataUpdate updatedMetadata = builder.Build();
                 //saving to cloud
                 ((PlayGamesPlatform)Social.Active).SavedGame.CommitUpdate(game, updatedMetadata, data, SavedGameWritten);
             //loading
             } else {
                 ((PlayGamesPlatform)Social.Active).SavedGame.ReadBinaryData(game, SavedGameLoaded);
             }
         //error
         } else {
             Debug.LogWarning("Error opening game: " + status);
         }
     }
 
     //callback from SavedGameOpened. Check if loading result was successful or not.
     private void SavedGameLoaded(SavedGameRequestStatus status, byte[] data) {
         if (status == SavedGameRequestStatus.Success){
             Debug.Log("SaveGameLoaded, success=" + status);
             ProcessCloudData(data);
         } else {
             Debug.LogWarning("Error reading game: " + status);
         }
     }
 
     //callback from SavedGameOpened. Check if saving result was successful or not.
     private void SavedGameWritten(SavedGameRequestStatus status, ISavedGameMetadata game) {
         if (status == SavedGameRequestStatus.Success){
             Debug.Log("Game " + game.Description + " written");
         } else {
             Debug.LogWarning("Error saving game: " + status);
         }
     }
 
     //merge local save with cloud save. Here is where you change the merging betweeen cloud and local save for your setup.
     private void MergeWith(string other) {
         if (other != "") {
             saveString = other;
         } else {
             Debug.Log("Loaded save string doesn't have any content");
         }
     }
 
     //return saveString as bytes
     private byte[] ToBytes() {
         byte[] bytes = Encoding.UTF8.GetBytes(saveString);
         return bytes;
     }
 
     //take bytes as arg and return string
     private string FromBytes(byte[] bytes) {
         string decodedString = Encoding.UTF8.GetString(bytes);
         return decodedString;
     }
 
     // -------------------- ### Extra UI for testing ### -------------------- 
 
     //call this with Unity button to view all saves on GPG. Bug: Can't close GPG window for some reason without restarting.
     public void showUI() {
         // user is ILocalUser from Social.LocalUser - will work when authenticated
         ShowSaveSystemUI(Social.localUser, (SelectUIStatus status, ISavedGameMetadata game) => {
             // do whatever you need with save bundle
         });
     }
     //displays savefiles in the cloud. This will only include one savefile if the m_saveName hasn't been changed
     private void ShowSaveSystemUI(ILocalUser user, Action<SelectUIStatus, ISavedGameMetadata> callback) {
         uint maxNumToDisplay = 3;
         bool allowCreateNew = true;
         bool allowDelete = true;
 
         ISavedGameClient savedGameClient = PlayGamesPlatform.Instance.SavedGame;
 
         if (savedGameClient != null) {
             savedGameClient.ShowSelectSavedGameUI(user.userName + "\u0027s saves",
                 maxNumToDisplay,
                 allowCreateNew,
                 allowDelete,
                 (SelectUIStatus status, ISavedGameMetadata saveGame) => {
                     // some error occured, just show window again
                     if (status != SelectUIStatus.SavedGameSelected) {
                         ShowSaveSystemUI(user, callback);
                         return;
                     }
 
                     if (callback != null)
                         callback.Invoke(status, saveGame);
                 });
         } else {
             // this is usually due to incorrect APP ID
             Debug.LogError("Save Game client is null...");
         }
     }
 
 }
 #endif
Comment
ResoDev
asotelo94
sten360
Axtrainz
forsu
Bob-The-Zealot
alphadogware
Spiffai
wenderRondonia
Nan77777
VGMFR
Niloy
AAAAAAAAAE
MaJr85
henryfjones
And 3 more...

People who like this

18 Show 13 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Leonid · Sep 04, 2016 at 04:09 PM 0
Share

Hi! Thanks for this code.

I just tried it, but logcat shows error:

"09-04 21:00:00.998: I/Unity(12896): NotImplementedException: You must enable saved games before it can be used. See PlayGamesClientConfiguration.Builder.EnableSavedGames."

and i have no callbacks on OpenWithAutomaticConflictResolution

"Save games" is activated in Dev console and .EnableSavedGames() is enabled in PlayGamesPlatform config

Can you please tell me, why this problem may occure?

avatar image Leonid Leonid · Sep 04, 2016 at 05:02 PM 1
Share

SOLVED! Looks like problem was with calling PlayGamesPlatform.Activate(); AFTER logging in. That line must be before logging in.

avatar image C4nDiM4n Leonid · Sep 05, 2016 at 07:33 AM 0
Share

Ah nice, good catch. I didn't include code for login to keep it small.

avatar image sten360 · Nov 20, 2016 at 12:09 AM 1
Share

Hi!

While I was already halfway there myself, your code definitely helped me make more sense of everything and figure out what I can customize and play around with. This kind of stripped down code with some simple explanation was exactly what I needed. I cannot thank you enough!

avatar image HAEGOE · Dec 13, 2017 at 01:10 PM 0
Share

Thanks, I was looking for this as well.

One question though, what should I do if I want to save mutiple variables without using json? I'm using playerprefs for my save, (obviously) I have many variables to save for my game, and I don't know how to convert it to json.

avatar image alphadogware · Jan 11, 2018 at 11:00 PM 0
Share

UPVOTE!! You rock dude!!! I have been googling for hours trying to just find a way to save a simple value to GPG. Thanks.

avatar image alphadogware alphadogware · Jan 15, 2018 at 09:10 PM 0
Share

I hope that some one is still viewing this thread cause I have a question.

How secure is the values written? I mean, can the user go in and edit it since it's technically a save on his account?

avatar image peteradamondy alphadogware · Jan 16, 2018 at 11:39 AM 1
Share

You mean standard user/player? No. Although it is technically saved to his account. That part is not directly accessible by user. Also, he would need class (or at least marshalling structure) you are using for serialization.

With that being said, however it is possible to "hack/reverse eng. your apk" and have access to saves, scores and leaderboards. How to prevent that to happen is whole another topic.

Show more comments
avatar image Spiffai · Jan 26, 2018 at 03:12 PM 0
Share

Wow. Thank you SO much. This is super helpful.

Show more comments
avatar image

Answer by peteradamondy · Feb 18, 2015 at 08:43 PM

I've been struggling with GPG in unity too. Today, I finally figured t$$anonymous$$ngs out. SaveDataBundle should be your "save file" implementation with custom serialization/deserialization. Hope t$$anonymous$$s helps :)

 using UnityEngine;
 using System.Collections;
 using GooglePlayGames;
 using GooglePlayGames.BasicApi.SavedGame;
 using UnityEngine.SocialPlatforms;
 using System;
 using System.Collections.Generic;
 
 public class AndroidSaveSystem {
     
     public static Action                     OnSaveGameSelected;
     public static Action<SaveDataBundle>      OnSaveLoaded;
 
     private static SaveDataBundle             m_currentSaveBundle;
     private static ISavedGameMetadata         m_saveBundleMetadata;
 
 
     /// <summary>
     /// Static reference to current save data. Automatically refreshed by save system.
     /// </summary>
     /// <value>The current save.</value>
     public SaveDataBundle CurrentSave
     {
         get
         {
             return m_currentSaveBundle;
         }
     }
 
     /// <summary>
     /// Shows the default save system UI. T$$anonymous$$s allows user to select/delete saves as required.
     /// </summary>
     /// <param name="user">User.</param>
     /// <param name="callback">Invokes, when save game has been selected. Check for status for errors, or </param>
     public void ShowSaveSystemUI(ILocalUser user, Action<SelectUIStatus,ISavedGameMetadata> callback)
     {
         uint maxNumToDisplay = 3;
         bool allowCreateNew = true;
         bool allowDelete = true;
 
         ISavedGameClient savedGameClient = PlayGamesPlatform.Instance.SavedGame;
 
         if(savedGameClient != null)
         {
             savedGameClient.ShowSelectSavedGameUI(user.userName + "\u0027s saves",
                                                   maxNumToDisplay,
                                                   allowCreateNew,
                                                   allowDelete,
             (SelectUIStatus status, ISavedGameMetadata saveGame) =>
             {
                 // some error occured, just show window again
                 if(status != SelectUIStatus.SavedGameSelected)
                 {
                     ShowSaveSystemUI(user,callback);
                     return;
                 }
 
                 if(callback != null)
                     callback.Invoke(status,saveGame);
 
                 if(OnSaveGameSelected != null && status == SelectUIStatus.SavedGameSelected)
                     OnSaveGameSelected.Invoke();
             });
 
         }else{
             // t$$anonymous$$s is usually due to incorrect APP ID
             Debug.LogError("Save Game client is null...");
         }
     }
 
     /// <summary>
     /// Creates the new save. Save returned in callback is closed!. Open it before use.
     /// </summary>
     /// <param name="save">Save.</param>
     /// <param name="saveCreatedCallback">Invoked when save has been created.</param>
     private static void CreateNewSave(ISavedGameMetadata save, Action<ISavedGameMetadata> saveCreatedCallback)
     {
         ISavedGameClient savedGameClient = PlayGamesPlatform.Instance.SavedGame;
          
         SavedGameMetadataUpdate.Builder builder = new SavedGameMetadataUpdate.Builder ();
         builder = builder
             .WithUpdatedPlayedTime (save.TotalTimePlayed.Add (new TimeSpan (0, 0, (int)Time.realtimeSinceStartup)))
             .WithUpdatedDescription ("Saved at " + DateTime.Now);
         
         SavedGameMetadataUpdate updatedMetadata = builder.Build ();
 
         SaveDataBundle newBundle = new SaveDataBundle();
         newBundle.RegenerateSaveData(50);
         savedGameClient.CommitUpdate ( save, 
                                        updatedMetadata, 
                                        SaveDataBundle.ToByteArray (newBundle), 
                                       (SavedGameRequestStatus status,ISavedGameMetadata game)=>
                                       {
             if(status == SavedGameRequestStatus.Success)
             {
                 m_saveBundleMetadata = game;
                 if(saveCreatedCallback != null)
                     saveCreatedCallback(game);
             }
             Debug.Log("Creating new save finished with status :" + status.ToString());
         });
     }
 
     /// <summary>
     /// Opens the saved game.
     /// </summary>
     /// <param name="savedGame">Saved game.</param>
     /// <param name="callback">Invoked when game has been opened</param>
     private static void OpenSavedGame(ISavedGameMetadata savedGame, Action<ISavedGameMetadata> callback)
     {
         if(savedGame == null)
             return;
 
         if(!savedGame.IsOpen)
         {
             ISavedGameClient saveGameClient = PlayGamesPlatform.Instance.SavedGame;
 
             // save name is generated only when save has not been commited yet
             saveGameClient.OpenWithAutomaticConflictResolution(
                 savedGame.Filename == string.Empty ? "Save" + UnityEngine.Random.Range(1000000,9999999).ToString() : savedGame.Filename,
                 DataSource.ReadCacheOrNetwork,
                 ConflictResolutionStrategy.UseLongestPlaytime,
                 (SavedGameRequestStatus reqStatus, ISavedGameMetadata openedGame) =>
                 {
                     if(reqStatus == SavedGameRequestStatus.Success)
                     {
                         m_saveBundleMetadata = openedGame;
                         if(callback != null)
                             callback.Invoke(m_saveBundleMetadata);
                     }
                 });
         }else{
             if(callback != null)
                 callback.Invoke(savedGame);
         }
     }
 
 
     /// <summary>
     /// Loads the saved game. T$$anonymous$$s should be a starting point for loading data.
     /// </summary>
     /// <param name="user">User.</param>
     /// <param name="onSaveLoadedCallback">On save loaded callback.</param>
     public void LoadSavedGame (ILocalUser user, Action<SaveDataBundle> onSaveLoadedCallback)
     {
 
         if(onSaveLoadedCallback != null)
             OnSaveLoaded += onSaveLoadedCallback;
 
         if(m_saveBundleMetadata == null)
         {
             ShowSaveSystemUI (user, LoadGame);
         }else
         {
             LoadGame(SelectUIStatus.SavedGameSelected, m_saveBundleMetadata);
         }
     }
 
     static void LoadGame (SelectUIStatus status, ISavedGameMetadata game)
     {
 
         if (status == SelectUIStatus.SavedGameSelected){
 
             OpenSavedGame (game, (ISavedGameMetadata openedGame) => 
             {
                 if(game.Description == null || game.Description == string.Empty)
                 {
                     // game has not been saved on cloud before, create new file
                     CreateNewSave(openedGame, (ISavedGameMetadata newlySavedGame) =>
                                   {
                         LoadGame(SelectUIStatus.SavedGameSelected, newlySavedGame);
                     });
                     return;
                 }
 
                 // save should be opened now
                 Debug.Log ("Loading save from: " + openedGame.Filename + "\n" + openedGame.Description + "\nOpened = " + openedGame.IsOpen.ToString ());
                 m_saveBundleMetadata = openedGame;
                 ISavedGameClient savedGameClient = PlayGamesPlatform.Instance.SavedGame;
                 savedGameClient.ReadBinaryData (openedGame, (SavedGameRequestStatus reqStatus, byte[] data) => 
                 {
                     Debug.Log("Reading save finished with status: " + reqStatus.ToString());
 
                     if (reqStatus == SavedGameRequestStatus.Success){
                         SaveDataBundle bundle = SaveDataBundle.FromByteArray (data);
                         m_currentSaveBundle = bundle;
                         if (OnSaveLoaded != null)
                         {
                             OnSaveLoaded.Invoke (bundle);
                             OnSaveLoaded = null;
                         }
                     }
                 });
             });
         }
     }
 
     public void SaveGame (SaveDataBundle file, Action<bool> callback)
     {
         CommitSaveToCloud(file,"undefined",callback);
     }
 
 
     /// <summary>
     /// Commits the save to cloud.
     /// </summary>
     /// <param name="file">Actual save file. T$$anonymous$$s will replace static reference to current save file</param>
     /// <param name="fileName">File name. Used only when saving for first time</param>
     /// <param name="callback">Invoked after commit (true = success)</param>
     private static void CommitSaveToCloud (SaveDataBundle file, string fileName, System.Action<bool> callback)
     {
         ISavedGameClient savedGameClient = PlayGamesPlatform.Instance.SavedGame;
 
         savedGameClient.OpenWithAutomaticConflictResolution(
                 m_saveBundleMetadata.Filename == string.Empty ? fileName : m_saveBundleMetadata.Filename,
                 DataSource.ReadCacheOrNetwork,
                 ConflictResolutionStrategy.UseLongestPlaytime,
                 (SavedGameRequestStatus reqStatus, ISavedGameMetadata openedGame) =>
                 {
                     if(reqStatus == SavedGameRequestStatus.Success)
                     {
                         // adding real time since startup so we can determine longes playtime and resolve future conflicts easilly
                         m_saveBundleMetadata = openedGame; 
                         SavedGameMetadataUpdate.Builder builder = new SavedGameMetadataUpdate.Builder ();
                         builder = builder
                             .WithUpdatedPlayedTime (m_saveBundleMetadata.TotalTimePlayed.Add (new TimeSpan (0, 0, (int)Time.realtimeSinceStartup)))
                             .WithUpdatedDescription ("Saved game at " + DateTime.Now);
                         
                         SavedGameMetadataUpdate updatedMetadata = builder.Build ();
                         
                         savedGameClient.CommitUpdate ( m_saveBundleMetadata,  
                                                       updatedMetadata, 
                                                       SaveDataBundle.ToByteArray (file),  
                                                       (SavedGameRequestStatus status,ISavedGameMetadata game)=>
                                                       {
                                 if(status == SavedGameRequestStatus.Success)
                                 {
                                     m_saveBundleMetadata = game;
                                     m_currentSaveBundle = file;
                                 }
                                 if(callback != null)
                                     callback.Invoke(status == SavedGameRequestStatus.Success);
                         });
                     }
             });
     }
 }
 

 





Comment
starikcetin
furic
Spiffai

People who like this

3 Show 11 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image el_saq · Feb 19, 2015 at 06:45 PM 0
Share

Great Help! I have a few questions:

If I understand right, SaveDataBundle is another Script (SaveDataBundle.cs) with the functions ToByteArray(), FromByteArray() and RegenerateSaveData() (for what is this one??).

Something like this:

 public class SaveDataBunndle 
 {
    public void RegenerateSaveData () {}
 
    public byte[] ToByteArray() {
             return System.Text.ASCIIEncoding.Default.GetBytes(ToString());
         }
         
         
    public static GameData FromByteArray (byte[] bytes) {
             return FromString(System.Text.ASCIIEncoding.Default.GetString(bytes));
         }
 }

Imagine I have a public float Coins, where I have to put that float (that one is the one I want to save in the cloud).

Thanks for your time!

avatar image peteradamondy · Feb 20, 2015 at 09:43 AM 0
Share

You don't have to use RegenerateSaveData. This is just method I use to populate save structure. You can use simple code like this:

 [Serializable]
 public class SaveDataBundle
 {
     // public members are serialized automatically
     // for private members use [SerializeField] attribute
     public float     m_coins;
     
     
     public SaveDataBundle(float initialCoinAmount)
     {
         
         m_coins = initialCoinAmount;
     }
     
     public static SaveDataBundle FromByteArray(Byte[] array)
     {
 
         if(array == null || array.Length == 0)
         {
             Debug.LogWarning("Serialization of zero sized array!");
             return null; 
         }
         
         using(var stream = new System.IO.MemoryStream(array))
         {
             try{
                        var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                 SaveDataBundle bundle = (SaveDataBundle)formatter.Deserialize(stream);
                 return bundle;
             }catch(Exception e)
             {
                 Debug.LogError("Error when reading stream: "+ e.Message);
             }
         }
         return null;
     }
     
     public static byte[] ToByteArray( SaveDataBundle bundle)
     {
         var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
         using(var stream = new System.IO.MemoryStream())
         {
             formatter.Serialize(stream, bundle );
             return stream.ToArray();
         }
     }
 }
avatar image el_saq · Feb 20, 2015 at 11:19 AM 0
Share

Thanks for your time!

From the first code that you provides me, how can I use a button to display save o load game?

The problem is that the functions have arguments and that is imcompatible with UI buttons of Unity 4.6.

avatar image peteradamondy · Feb 20, 2015 at 11:33 AM 0
Share

You can simply wrap functionality like this:

 // reference to save system instance
 private static AndroidSaveSystem m_saveSystem;
 
 
 public void Start()
 {
     // create new instance of save system - it is static, so only one instance will be created in entire game
     if(m_saveSystem == null)
         m_saveSystem = new AndroidSaveSystem();
 }
 
 // call this method form load button
 public void LoadGame()
 {
 // user is ILocalUser from Social.LocalUser - will work when authenticated
     m_saveSystem.LoadSavedGame(user, (SaveDataBundle saveBundle) =>
          {
             // do whatever you need with save bundle
         } );
 }
 
 // call this method from save button
 public void Save()
 {
     if(m_saveSystem != null && m_saveSystem.CurrentSave != null)
           m_saveSystem.SaveGame(m_saveSystem.CurrentSave,OnLevelSaved);
 }
 
 
 private void OnLevelSaved(bool success)
 {
     // do whatever you need when game has been successfully saved
 }
avatar image el_saq · Feb 21, 2015 at 11:20 AM 1
Share

This plugin is really frustrating, I´m moving to a backend server service for my games, the support and tutorials of Google are shit.

Any one hace used GameSparks or Gamedonia?

Thanks!

Show more comments
avatar image

Answer by google_play_service_dev · Oct 03, 2016 at 02:10 PM

You can try another Play game service Unity plugin

https://github.com/unity-plugins/google-play-game-service-unity-plugin/wiki/google--Play-Game-Service-unity-plugin-Tutorial

Login Google Play Game

Login Game is required before call other API.Play Game Unity Plugin support google account ,not support google plus api now.

 GoogleGame.Instance().login (true, false);
 GoogleGame.Instance().gameEventHandler += onGameEvent;
 void onGameEvent(int result_code,string eventName,string data){
     Debug.Log (eventName + "-----------" + data);
     if(result_code==-1&&eventName==GameEvent.onConnectSuccess){
         //login success,you can do other now
     }
 }

You can get player infomation after login,the data is json format string

 string json=GoogleGame.Instance().getCurrentUserInfo();

If you exit your game,you can login Out and disconnect

 GoogleGame.Instance().loginOut();

Play Game Leaderboard

Display Leaderboard with default UI is very easy

 GoogleGame.Instance().showLeaderboards();

Submit a leaderboard score,the first param is leaderboard id,the second param is score value.

 GoogleGame.Instance().submitLeaderboardScore("CgkItJ_UzNUHEAIQCQ", 1000L);

if you want to define a ui for leaderboard,you can load data,and you will get data in event GameEvent.onLeaderboardMetadataResult

 GoogleGame.Instance().loadLeaderboardsMetadata(false);

Play Game Ac$$anonymous$$eve

Display Ac$$anonymous$$evements with default UI is easy too

 GoogleGame.Instance().showAc$$anonymous$$evements();

Unlock Ac$$anonymous$$evement,the param is Ac$$anonymous$$evement ID

 GoogleGame.Instance().unlockAc$$anonymous$$evement("CgkItJ_UzNUHEAIQBA");

If you want to define ui for Ac$$anonymous$$evement,you can load data ,and handle event GameEvent.onLoadAc$$anonymous$$evementsResult

 GoogleGame.Instance().loadAc$$anonymous$$evements();

Game Event and Quest

 Load Game Event List,and handle Event GameEvent.onLoadEventsResult

 GoogleGame.Instance().loadEvents();

Change Event Data

 GoogleGame.Instance().incrementEvent("eventID",102);

Load Quest ,and handle Event GameEvent.onLoadQuestsResult.selector is in GameConst such as SELECT_COMPLETED,sortOrder is SORT_ORDER_MOST_RECENT_FIRST or SORT_ORDER_SOCIAL_AGGREGATION

 GoogleGame.Instance().loadQuests(int[] questSelectors, int sortOrder, bool forceReload);

accept Quest

 GoogleGame.Instance().acceptQuest(string questid);

you can listener the state change of quest by handle event GameEvent.onQuestCompleted

Game Snapshot

Display saved snapshot with default ui

 GoogleGame.Instance().showSnapshots("saved games", true, true, 10);

Save Game State with google play snapshot api.open snapshot first

 GoogleGame.Instance().openSnapshot("firstgamesnap", true, GameConst.RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED);

and then write snapshot after event onOpenSnapshotResult,snapshotfilePath is a image path,the second param is your game data

 GoogleGame.Instance().writeSnapshot(snapshotfilePath, System.Text.Encoding.UTF8.GetBytes("{'score':20}"));

open a snapshot first and then get you saved data

 GoogleGame.Instance().openSnapshot("firstgamesnap", true, GameConst.RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED);

after open success

 byte[] gamedata=GoogleGame.Instance().readSnapshot();

RealTime Multiplayer Game

Create a multiplayer game room,and show waiting Room Panel

 GoogleGame.Instance().createAutoMatchRoom(mincount,maxcount,mask);
 GoogleGame.Instance().showRoomWaitingPanel(minParticipantsToStart);

you can accept invite to enter a room too

 GoogleGame.Instance().acceptInviteToRoom(inviteid);

if the player want to leave the room

 GoogleGame.Instance().leaveRoom();

There are many Event when play multiplayer game,such as onRoomCreated,onJoinedRoom,and you can handle event to play game when GameEvent.onRoomWaitingChange fired. google game support realtime message reliable or unreliable.if recipientParticipantId is null,then message will been sent to all player except sender.

 GoogleGame.Instance().sendReliableMessage(byte[] messageData, string roomId, string recipientParticipantId);
 //GoogleGame.Instance().sendUnreliableMessage(byte[] messageData, string roomId, string[] recipientParticipantIds);

Turnbased Multiplayer Game

Create a turnbased game room

 GoogleGame.Instance().createTurnBasedMatch(minplayer, maxplayer, mask);

or you can accept a Invitation

 GoogleGame.Instance().acceptTurnBasedInvitation(invitationId);

Show All turn based Matches with default UI

 GoogleGame.Instance().showTurnBasedMatches();

Show TurnBased Invitations

 GoogleGame.Instance().showTurnBasedInvitations( minPlayers, maxPlayers,  exclusiveBitMask, allowAutomatch);

There are many event you can handle such as onInitiateMatchResult,onUpdateMatchResult,when onTurnBasedMatchReceived received you can do game logic

when play game,you will call taketurnbasedturn to notify the next player

 GoogleGame.Instance().takeTurnBasedTurn( matchId, matchData,  pendingParticipantId);

GoogleGame source

google realtimeMultiplayer api

Comment
lifeisfunxyz
Digheadsferke
MaJr85

People who like this

3 Show 0 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

19 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Scripted Ball Bouncing igher and higher 0 Answers

[SOLVE] OnMouseEnter not working on UI elements 2 Answers

Which script is calling destroy on my GameObject? 2 Answers

Problem with attaching scripts to objects 1 Answer

Audio delay after loading ogg external movie at runtime 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges