1

I need a little help. I'm making a simple enough game in Unity3D and I'd like to save a few variables (gold, inventory and such) using SavedGames (Google Games Services). I've read about it and apparently I have to send the data to Google Games as a byte array. So I've read about bytes arrays too, but I don't understand it. Every article or question about it seems to assume a certain degree of knowledge about it (which I do not have obviously). I'm stuck. My question is : how do you go from int variables to bytes arrays?

For example: User has 52 gold coins (int gold_coins), three healing potions (int heal_pot) and 4 mana potions (int mana_pot). I want to put those three variables into a byte array that I can send add to Google when the user saves the game state. Then, when he loads it again the data from the byte array needs to go back to the ints. (That last part I can probably figure out by myself when I see how to put them into the array in the first place).

I hope you guys can explain it to me or point me into the right direction. In the meantime, have a nice monday. Thank you.

EDIT 2: So, I've got this :

in SaveData.cs :

using UnityEngine;
using ProtoBuf;

[ProtoContract]
public enum DataType{
    pu0 = 0; //gold in game
    pu1 = 1; //heal_pot ingame
    pu2 = 2; //mana_pot ingame
}

[ProtoContract]
public class PBSaveData{
    [ProtoMember(1)]
    public DataType type;
    [ProtoMember(2)]
    public int amountStored;
}

in PBGameState.cs:

using ProtoBuf;

[ProtoContract]
public class PBGameState{
    [ProtoMember(1)]
    public PBSaveData[] saveData;
}

in SaveManager.cs:

using ProtoBuf;
using System.IO;

public class SaveManager{
    private string gameStateFilePath;

    void SaveState(){
        PBStateGame state = new PBGameState();
        state.saveData = new PBSaveData[3];
        for (int i=0; i<state.saveData.Length{
            state.saveData[i] = new PBSaveData();
            state.saveData[i].type = "pu" + (i+1);
            state.saveData[i].amoundStore = its[i] //its is where is store my inventory locally.
        }
        using(var ms = new MemoryStream()){
        // Serialize the data to the stream
            Serializer.Serialize(ms, state);
            byteArray = ms.ToArray();
        }
        // Create a texture the size of the screen, RGB24 format
        int width = Screen.width;
        int height = Screen.height;
        Texture2D Screenshot = new Texture2D( width, height, TextureFormat.RGB24, false );
        // Read screen contents into the texture
        Screenshot.ReadPixels( new Rect(0, 0, width, height), 0, 0 );
        Screenshot.Apply();


        long TotalPlayedTime = 20000;
        string currentSaveName =  "sg";
        string description  = "Modified data at: " + System.DateTime.Now.ToString("MM/dd/yyyy H:mm:ss");


        GooglePlaySavedGamesManager.ActionGameSaveResult += ActionGameSaveResult;
        GooglePlaySavedGamesManager.instance.CreateNewSnapshot(currentSaveName, description, Screenshot, byteArray, TotalPlayedTime);
    }
}

void LoadState()
{
    PBGameState state = new PBGameState ();

    using(var ms = new MemoryStream(byteArray)){
        state = Serializer.Deserialize<PBGameState> (ms);
        AndroidMessage.Create ("loadtest", state.saveData[0].amountStored.ToString());
    }

    if (state.saveData != null) {
        // Iterate through the loaded game state 
        for (int i = 0; i < state.saveData.Length; i++) {
            its [i] = state.saveData [i].amountStored;
        }
    } else {
        AndroidMessage.Create ("loadtest", "notworking");
    }
}

So saving works apparently, but when I restart the game, the data don't seem to be loaded (I have a button that starts the loading process) and the inventory is empty... Any idea what I did wrong? Thank you :)

Here is the link for the tutorial : tuto

Anyone please?

Jeryl
  • 139
  • 1
  • 10

2 Answers2

2

What you need to do is:

  • have an object to store the values you want to save, such as a class name GameState or whatever
  • serialize your instance of this class, which is the process of transforming your instance in your byte array
  • when you want to get your values back from the byte array, you need to deserialize your byte array

See for instance Convert any object to a byte[]

See also https://msdn.microsoft.com/en-us/library/ms233843.aspx


To answer to second question:

Instead of using a FileStream, use a MemoryStream:

// New MemoryStream, used just like any other stream, but without file (all in memory)
var ms = new MemoryStream();

// Serialize the data to the stream
Serializer.Serialize(fs, state);

// Get back the byte array from the stream
// Doesn't matter where the Position is on the stream, this'll return the
// whole MemoryStream as a byte array
var byteArray = ms.ToArray();
Community
  • 1
  • 1
ken2k
  • 48,145
  • 10
  • 116
  • 176
  • Watch out though, the accepted answer to the linked question recommends BinaryFormatter, which is only good for temporary storage and communication/remoting, not persistent storage. Using BF for longer term storage always ends in tears. Use a ContractSerializer or Protocol buffers or similar for persistence. – Anders Forsgren Dec 07 '15 at 10:45
  • The linked answer is a basic example of serialization that shows how the general process works. That said, as you noted, protobuf for instance is much more version-tolerant, which is something to take into account when you deal with persistent storage. – ken2k Dec 07 '15 at 10:51
  • Thanks both. Before I go reading on protobuf-net, would that work: – Jeryl Dec 07 '15 at 11:18
  • I have reedited the OP to where I am at : with protobug-net setup (i think) – Jeryl Dec 07 '15 at 13:33
  • It works, I'll edit the OP tomorrow with the code so that it can help others. Thank you so much. – Jeryl Dec 07 '15 at 20:38
  • @ken2k hello again, I manage to save data (apparently) but not load it. I've updated the OP. Would you mind give it a look, please? – Jeryl Dec 08 '15 at 08:59
  • Been trying variations of edit 2 code for two days. Still not working :'( – Jeryl Dec 10 '15 at 08:27
1

First, save your variables into a string in some specific format (such as seperated by semicolons). Then, convert it to a byte[] by using some encoding.

string saveState = String.Format("{0};{1};{2}", gold_coins, heal_pot, mana_pot);
byte[] saveBytes = Encoding.UTF8.GetBytes(saveState);

After that you can do as you wish with the byte[]. To parse the saved data:

string[] retrievedData = Encoding.UTF8.GetString(saveBytes).Split(';'); //saveBytes is the Byte[] you get from the cloud.
int gold = int.Parse(retrievedData[0]);

The parsed array will contain items in the order you put them in the string.

BUT this is is only a simple (and not at all recommended) way of serialization). For a better way see @ken2k's answer.

Fᴀʀʜᴀɴ Aɴᴀᴍ
  • 6,131
  • 5
  • 31
  • 52