Skip to content

Instantly share code, notes, and snippets.

@iaincollins
Last active August 9, 2024 17:56
Show Gist options
  • Save iaincollins/9661b5b6578fef5b344c37b8f2bde976 to your computer and use it in GitHub Desktop.
Save iaincollins/9661b5b6578fef5b344c37b8f2bde976 to your computer and use it in GitHub Desktop.
How to use Objects with Unity Cloud Save

How to use objects with Unity Cloud Save

A lot of developers trip up on how best to seralize and deseralize data when using Unity Cloud Save, specifically they often save objects as strings rather saving them directly as objects and sometimes run into problems as a result.

This happens either because people are used to how versions of the Cloud Save SDK used to work, because they don't realise that you can save objects without converting them to a string, or because they don't realise that the approach they are using causes them to be seralized.

If saved as a string instead of directly as an object, data can be more complicated to work with than it needs to be, you won't be able to use features like filters to query on the saved data and if you are viewing in somewhere like the Unity Cloud Dashboard it will just be rendered as a single long escaped string (which is hard to read and even harder to edit).

The sample that comes with the Unity Cloud Save SDK has good examples of how to load and save objects that includes appropriate error handling, but for quick reference (without any error handling!) some people might find this example helpful.

Example models

These are the models that define the objects we will be saving and loading.

using System;
using System.Collections.Generic;

[Serializable]
public class SampleObject
{
  public string Name;
  public List<SampleItem> Items = new List<SampleItem>();
}

public class SampleItem
{
  public string ItemName;
  public int ItemInt;
  public float ItemFloat;
  public bool ItemBool;
}

How to save and load objects from Cloud Save

This code includes an example of how to write locks when saving data, to ensure it hasn't already been modified by another process or thread between reading and writing. If you don't use write locks, it will just force save.

using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Unity.Services.Authentication;
using Unity.Services.CloudSave;
using Unity.Services.CloudSave.Models;
using Unity.Services.Core;
using UnityEngine;

public class CloudSaveObjectExample: MonoBehaviour
{
  private async void Awake()
  {
    // Sign in with Unity Authentication
    await UnityServices.InitializeAsync();
    await AuthenticationService.Instance.SignInAnonymouslyAsync();

    // Create a new sample object
    SampleObject newSampleObject = new SampleObject { Name = "My sample object" };
    newSampleObject.Items.Add(
      new SampleItem
     {
        ItemName = "Item 1",
        ItemInt = 1,
        ItemFloat = 3.14f,
        ItemBool = true
     }
   );

    // Save the object to Cloud Save Player Data, get a write lock back
    string writeLock = await SaveSampleObject("myKey", newSampleObject);
    Debug.Log($"Saved {JsonConvert.SerializeObject(newSampleObject)} and got write lock {writeLock}");

    // Load sample object from Cloud Save Player Data
    SampleObject sampleObject = await LoadSampleObject<SampleObject>("myKey");
    Debug.Log($"Loaded {JsonConvert.SerializeObject(sampleObject)}");

    // Modify the item we just got back from Cloud Save
    sampleObject.Items.Add(
        new SampleItem
        {
          ItemName = "Item 2",
          ItemInt = 2,
          ItemFloat = 4.28f,
          ItemBool = false
        }
    );

    // Save the modified item back to Cloud Save
    writeLock = await SaveSampleObject("myKey", sampleObject, writeLock);
    Debug.Log($"Saved {JsonConvert.SerializeObject(sampleObject)} and got new write lock {writeLock}");
  }

  private async Task<string> SaveSampleObject(string key, object value, string writeLock = null)
  {
    Dictionary<string, string> result = await CloudSaveService.Instance.Data.Player.SaveAsync(
      new Dictionary<string, SaveItem> { { key, new SaveItem(value, writeLock) } }
    );
    return result[key]; // return write lock
  }

  private async Task<T> LoadSampleObject<T>(string key)
  {
    var results = await CloudSaveService.Instance.Data.Player.LoadAsync(new HashSet<string> { key });

    if (results.TryGetValue(key, out var item))
    {
      return item.Value.GetAs<T>();
    }
    else
    {
      return default;
    }
  }

}

The saved data will look something like like this when viewed in the Unity Cloud Dashboard (i.e. nicely formatted and easily editable, strings won't be double-escaped):

image

Tips

  • You don't need to serialize objects first, the Unity Cloud Save SDK will do that for you
  • Data in Cloud Save can be Strings, Numbers, Arrays, Booleans or Objects
  • Objects can be nested and you can have arrays of objects

See the Unity Cloud Save tutorial for more information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment