This post contains all the code that’s been written in the YouTube video.
You can read it at your own pace, although watching the video prior to looking at this code example is recommended.
JsonUtil.cs
using System.Collections;
using Boomlagoon.JSON;
using System;
public class JsonUtil {
public static string CollectionToJsonString<T>(T arr, string jsonKey) where T : IList
{
JSONObject jObject = new JSONObject();
JSONArray jArray = new JSONArray();
for (int i = 0; i < arr.Count; i++)
jArray.Add(new JSONValue(arr[i].ToString()));
jObject.Add(jsonKey, jArray);
return jObject.ToString();
}
public static T[] JsonStringToArray<T>(string jsonString, string jsonKey,
Func<string, T> parser)
{
JSONObject jObject = JSONObject.Parse(jsonString);
JSONArray jArray = jObject.GetArray(jsonKey);
T[] convertedArray = new T[jArray.Length];
for (int i = 0; i < jArray.Length; i++)
convertedArray[i] = parser(jArray[i].Str.ToString());
return convertedArray;
}
}
PlayGamesScript.cs
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using GooglePlayGames.BasicApi.SavedGame;
using System.Text;
using UnityEngine;
public class PlayGamesScript : MonoBehaviour
{
public static PlayGamesScript Instance { get; private set; }
const string SAVE_NAME = "Tutorial3";
bool isSaving;
bool isCloudDataLoaded = false;
// Use this for initialization
void Start()
{
Instance = this;
//setting default value, if the game is played for the first time
if (!PlayerPrefs.HasKey(SAVE_NAME))
PlayerPrefs.SetString(SAVE_NAME, string.Empty);
//tells us if it's the first time that this game has been launched after install - 0 = no, 1 = yes
if (!PlayerPrefs.HasKey("IsFirstTime"))
PlayerPrefs.SetInt("IsFirstTime", 1);
LoadLocal(); //we want to load local data first because loading from cloud can take quite a while, if user progresses while using local data, it will all
//sync in our comparating loop in StringToGameData(string, string)
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder()
.EnableSavedGames().Build();
PlayGamesPlatform.InitializeInstance(config);
PlayGamesPlatform.Activate();
SignIn();
}
void SignIn()
{
//when authentication process is done (successfuly or not), we load cloud data
Social.localUser.Authenticate(success => { LoadData(); });
}
#region Saved Games
//making a string out of game data (highscores...)
string GameDataToString()
{
return JsonUtil.CollectionToJsonString(CloudVariables.ImportantValues, "myKey");
}
//this overload is used when user is connected to the internet
//parsing string to game data (stored in CloudVariables), also deciding if we should use local or cloud save
void StringToGameData(string cloudData, string localData)
{
if (cloudData == string.Empty)
{
StringToGameData(localData);
isCloudDataLoaded = true;
return;
}
int[] cloudArray = JsonUtil.JsonStringToArray(cloudData, "myKey", str => int.Parse(str));
if (localData == string.Empty)
{
CloudVariables.ImportantValues = cloudArray;
PlayerPrefs.SetString(SAVE_NAME, cloudData);
isCloudDataLoaded = true;
return;
}
int[] localArray = JsonUtil.JsonStringToArray(localData, "myKey", str => int.Parse(str));
//if it's the first time that game has been launched after installing it and successfuly logging into Google Play Games
if (PlayerPrefs.GetInt("IsFirstTime") == 1)
{
//set playerpref to be 0 (false)
PlayerPrefs.SetInt("IsFirstTime", 0);
for (int i = 0; i < cloudArray.Length; i++)
if (cloudArray[i] > localArray[i]) //cloud save is more up to date
{
//set local save to be equal to the cloud save
PlayerPrefs.SetString(SAVE_NAME, cloudData);
}
}
//if it's not the first time, start comparing
else
{
for (int i = 0; i < cloudArray.Length; i++)
//comparing integers, if one int has higher score in it than the other, we update it
if (localArray[i] > cloudArray[i])
{
//update the cloud save, first set CloudVariables to be equal to localSave
CloudVariables.ImportantValues = localArray;
isCloudDataLoaded = true;
//saving the updated CloudVariables to the cloud
SaveData();
return;
}
}
//if the code above doesn't trigger return and the code below executes,
//cloud save and local save are identical, so we can load either one
CloudVariables.ImportantValues = cloudArray;
isCloudDataLoaded = true;
}
//this overload is used when there's no internet connection - loading only local data
void StringToGameData(string localData)
{
if (localData != string.Empty)
CloudVariables.ImportantValues = JsonUtil.JsonStringToArray(localData, "myKey",
str => int.Parse(str));
}
//used for loading data from the cloud or locally
public void LoadData()
{
//basically if we're connected to the internet, do everything on the cloud
if (Social.localUser.authenticated)
{
isSaving = false;
((PlayGamesPlatform)Social.Active).SavedGame.OpenWithManualConflictResolution(SAVE_NAME,
DataSource.ReadCacheOrNetwork, true, ResolveConflict, OnSavedGameOpened);
}
//this will basically only run in Unity Editor, as on device,
//localUser will be authenticated even if he's not connected to the internet (if the player is using GPG)
else
{
LoadLocal();
}
}
private void LoadLocal()
{
StringToGameData(PlayerPrefs.GetString(SAVE_NAME));
}
//used for saving data to the cloud or locally
public void SaveData()
{
//if we're still running on local data (cloud data has not been loaded yet), we also want to save only locally
if (!isCloudDataLoaded)
{
SaveLocal();
return;
}
//same as in LoadData
if (Social.localUser.authenticated)
{
isSaving = true;
((PlayGamesPlatform)Social.Active).SavedGame.OpenWithManualConflictResolution(SAVE_NAME,
DataSource.ReadCacheOrNetwork, true, ResolveConflict, OnSavedGameOpened);
}
else
{
SaveLocal();
}
}
private void SaveLocal()
{
PlayerPrefs.SetString(SAVE_NAME, GameDataToString());
}
private void ResolveConflict(IConflictResolver resolver, ISavedGameMetadata original, byte[] originalData,
ISavedGameMetadata unmerged, byte[] unmergedData)
{
if (originalData == null)
resolver.ChooseMetadata(unmerged);
else if (unmergedData == null)
resolver.ChooseMetadata(original);
else
{
//decoding byte data into string
string originalStr = Encoding.ASCII.GetString(originalData);
string unmergedStr = Encoding.ASCII.GetString(unmergedData);
//parsing
int[] originalArray = JsonUtil.JsonStringToArray(originalStr, "myKey", str => int.Parse(str));
int[] unmergedArray = JsonUtil.JsonStringToArray(unmergedStr, "myKey", str => int.Parse(str));
for (int i = 0; i < originalArray.Length; i++)
{
//if original score is greater than unmerged
if (originalArray[i] > unmergedArray[i])
{
resolver.ChooseMetadata(original);
return;
}
//else (unmerged score is greater than original)
else if (unmergedArray[i] > originalArray[i])
{
resolver.ChooseMetadata(unmerged);
return;
}
}
//if return doesn't get called, original and unmerged are identical
//we can keep either one
resolver.ChooseMetadata(original);
}
}
private void OnSavedGameOpened(SavedGameRequestStatus status, ISavedGameMetadata game)
{
//if we are connected to the internet
if (status == SavedGameRequestStatus.Success)
{
//if we're LOADING game data
if (!isSaving)
LoadGame(game);
//if we're SAVING game data
else
SaveGame(game);
}
//if we couldn't successfully connect to the cloud, runs while on device,
//the same code that is in else statements in LoadData() and SaveData()
else
{
if (!isSaving)
LoadLocal();
else
SaveLocal();
}
}
private void LoadGame(ISavedGameMetadata game)
{
((PlayGamesPlatform)Social.Active).SavedGame.ReadBinaryData(game, OnSavedGameDataRead);
}
private void SaveGame(ISavedGameMetadata game)
{
string stringToSave = GameDataToString();
//saving also locally (can also call SaveLocal() instead)
PlayerPrefs.SetString(SAVE_NAME, stringToSave);
//encoding to byte array
byte[] dataToSave = Encoding.ASCII.GetBytes(stringToSave);
//updating metadata with new description
SavedGameMetadataUpdate update = new SavedGameMetadataUpdate.Builder().Build();
//uploading data to the cloud
((PlayGamesPlatform)Social.Active).SavedGame.CommitUpdate(game, update, dataToSave,
OnSavedGameDataWritten);
}
//callback for ReadBinaryData
private void OnSavedGameDataRead(SavedGameRequestStatus status, byte[] savedData)
{
//if reading of the data was successful
if (status == SavedGameRequestStatus.Success)
{
string cloudDataString;
//if we've never played the game before, savedData will have length of 0
if (savedData.Length == 0)
//in such case, we want to assign default value to our string
cloudDataString = string.Empty;
//otherwise take the byte[] of data and encode it to string
else
cloudDataString = Encoding.ASCII.GetString(savedData);
//getting local data (if we've never played before on this device, localData is already
//string.Empty, so there's no need for checking as with cloudDataString)
string localDataString = PlayerPrefs.GetString(SAVE_NAME);
//this method will compare cloud and local data
StringToGameData(cloudDataString, localDataString);
}
}
//callback for CommitUpdate
private void OnSavedGameDataWritten(SavedGameRequestStatus status, ISavedGameMetadata game)
{
}
#endregion /Saved Games
}
UIScript.cs
using UnityEngine;
using UnityEngine.UI;
public class UIScript : MonoBehaviour {
public Text[] ValueTextArray;
public void UpdateAll()
{
for (int i = 0; i < CloudVariables.ImportantValues.Length; i++)
ValueTextArray[i].text = CloudVariables.ImportantValues[i].ToString();
}
public void Save()
{
PlayGamesScript.Instance.SaveData();
}
public void Increment(int index)
{
CloudVariables.ImportantValues[index]++;
ValueTextArray[index].text = CloudVariables.ImportantValues[index].ToString();
}
}

The link in your description don’t work. I can’t find boomlagoon json plugin. Help me please.
Boomlagoon JSON source:
https://github.com/cooladata/cooladata-unity/tree/master/CooladataTest/Assets/CoolaData/Scripts/Boomlagoon/JSON
Hey, how can i save multiple arrays ? I tried array of int arrays and not worked well :/
Thanks for sharing. I read many of your blog posts, cool, your blog is very good.
I don’t think the title of your article matches the content lol. Just kidding, mainly because I had some doubts after reading the article.
Your article helped me a lot, is there any more related content? Thanks! https://www.binance.com/register?ref=QCGZMHR6