PSIKLO
PSIKLO

Reputation: 27

C#: How can I rewrite my code for managing Game Data Serialization using an Array instead of a List in Unity?

I am fairly new to coding and I am currently working on creating scripts to serialize data to a hard drive. I have managed to get it working with a list with the type "Game". The Game class holds data that can be serialized. I have been trying to write this code to work with an array instead that has a size of 3. Unfortunately, I have been having trouble when I run my program and try to use my GUI. I just need a push in the right direction for how to approach rewriting my code. I know to start with:

public static Game[] savedGames = new Game[3];

But how do I get my definitions to work with using an array instead of a list.

//<SaveLoad.cs>

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System;

public static class SaveLoad {
    // Note: [ 07 / 20 / 2018]
    // 1. Make  an array of game saves index of 3. (To replace list) 
    // 2. Create definition for overwritesave()??
    // 3. Rewrite definitions Save/Load/Delete to work with an array
    // 4. Start working on custom GUI

public static List<Game> savedGames = new List<Game>(3);

public static void Save()
{
    savedGames.Add(Game.current);
    BinaryFormatter bf = new BinaryFormatter();
    FileStream file = File.Create(Application.persistentDataPath + 
    "/savedGames.gd");
    Debug.Log("The file is saved at " + Application.persistentDataPath);
    bf.Serialize(file, savedGames);
    file.Close();
}

public static void Load()
{
    if (File.Exists(Application.persistentDataPath + "/savedGames.gd"))
    {
        BinaryFormatter bf = new BinaryFormatter();
        FileStream file = File.Open(Application.persistentDataPath + 
        "/savedGames.gd", FileMode.Open);
        Debug.Log("The File is loaded from " + 
        Application.persistentDataPath);
        savedGames = (List<Game>)bf.Deserialize(file);
        file.Close();
    }
}

public static void DeleteSave(int index) {

    if (File.Exists(Application.persistentDataPath + "/savedGames.gd"))
    {
        BinaryFormatter bf = new BinaryFormatter();
        FileStream file = File.Open(Application.persistentDataPath + 
        "/savedGames.gd", FileMode.Open);
        savedGames = (List<Game>)bf.Deserialize(file);
        file.Close();
    }

    savedGames.RemoveAt(index); 
    // Removes element in List from the passed Index

    BinaryFormatter bf2 = new BinaryFormatter();
    FileStream file2 = File.Create(Application.persistentDataPath + 
    "/savedGames.gd"); //you can call it anything you want
    Debug.Log("Save file has been Updated at " + 
    Application.persistentDataPath);
    bf2.Serialize(file2, savedGames); // Saves Changes to Data
    file2.Close();
    }
 }

This is my script for the GUI. I currently have it working for a list but I am having trouble using it with an array as well. I need help finding a way to pass an index value from the menu to modify an array.

//<MainMenu.cs>

 using UnityEngine;
 using System;
 using UnityEngine.SceneManagement;

 public class MainMenu : MonoBehaviour {

 int index = 0;

 public enum Menu {
    MainMenu,
    NewGame,
    Continue,
    delete,
    caution
}

 public Menu currentMenu;

     void OnGUI () {

         GUILayout.BeginArea(new Rect(0,0,Screen.width, Screen.height));
         GUILayout.BeginHorizontal();
         GUILayout.FlexibleSpace();
         GUILayout.BeginVertical();
         GUILayout.FlexibleSpace();

        if(currentMenu == Menu.MainMenu) {

            GUILayout.Box("THE EIGHT");
            GUILayout.Space(10);

        if(GUILayout.Button("New Game")) {
            Game.current = new Game();
            currentMenu = Menu.NewGame;
        }

        if(GUILayout.Button("Continue")) {
            SaveLoad.Load();
            currentMenu = Menu.Continue;
        }

        if(GUILayout.Button("Delete Save")) {
            SaveLoad.Load();
            currentMenu = Menu.delete;
        }

        if(GUILayout.Button("Quit")) {
            Application.Quit();
        }
    }

    else if (currentMenu == Menu.NewGame) {

        GUILayout.Box("Name Your Characters");
        GUILayout.Space(10);

        GUILayout.Label("Some Strange Boy");
        Game.current.Nestor.name = 
        GUILayout.TextField(Game.current.Nestor.name, 20);
        GUILayout.Label("Some Strange Girl");
        Game.current.SomeGirl.name = 
        GUILayout.TextField(Game.current.SomeGirl.name, 20);
        GUILayout.Label("Some Strange Friend");
        Game.current.SomeBro.name = 
        GUILayout.TextField(Game.current.SomeBro.name, 20);

        if(GUILayout.Button("Save")) {
            //Save the current Game as a new saved Game
            SaveLoad.Save();
            //Move on to game...
            SceneManager.LoadScene(2);
        }

        GUILayout.Space(10);
        if(GUILayout.Button("Cancel")) {
            currentMenu = Menu.MainMenu;
        }

    }

    else if (currentMenu == Menu.Continue) {

        GUILayout.Box("Select Save");
        GUILayout.Space(10);

        foreach(Game g in SaveLoad.savedGames) {
            if(GUILayout.Button(g.Nestor.name + " - " + g.SomeGirl.name + " - " + g.SomeBro.name)) {
                Game.current = g;
                //Move on to game...
                SceneManager.LoadScene(2);
            }

        }

        GUILayout.Space(10);
        if(GUILayout.Button("Cancel")) {
            currentMenu = Menu.MainMenu;
        }

    }

    else if (currentMenu == Menu.delete)
    {

        GUILayout.Box("Select Save to Delete");
        GUILayout.Space(10);

        foreach (Game g in SaveLoad.savedGames)
        {
            if (GUILayout.Button(g.Nestor.name + " - " + g.SomeGirl.name + " 
            - " + g.SomeBro.name))
            {
                index = SaveLoad.savedGames.IndexOf(g);
                Debug.Log(index);
                currentMenu = Menu.caution;
            }

        }

        GUILayout.Space(10);
        if (GUILayout.Button("Cancel"))
        {
            currentMenu = Menu.MainMenu;
        }

    }
else if(currentMenu == Menu.caution) {

        GUILayout.Box("Are you sure?");
        GUILayout.Space(10);
        if (GUILayout.Button("Yes")) {
            Debug.Log(index);
            SaveLoad.DeleteSave(index);
            currentMenu = Menu.MainMenu;
        }

           GUILayout.Space(10);
            if (GUILayout.Button("No")) {
                currentMenu = Menu.delete;
            }

        }

        GUILayout.FlexibleSpace();
        GUILayout.EndVertical();
        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();
        GUILayout.EndArea();

    }
}

Upvotes: 1

Views: 132

Answers (1)

Bennett Yeo
Bennett Yeo

Reputation: 829

You pretty much only need to replace your cast of a List<Game> to a Game[]. Here's some (error unhandled) sample code infrastructure to get you started. As a hint, wrapping your file/serialization code in try, catch with IOException and SerializationException should take care of the brunt of normal user case scenarios. It may be worth trying to mess with your .bin files just to see what happens and test various corner case functionality of your code.

void Awake() {
    //this is an object initializer, pass your Game[] in place of this
    Save(new[] {
        new Game { propertyStub = 0 },
        new Game { propertyStub = 1 },
        new Game { propertyStub = 2 }
    });

    var gA = Load();
    if(gA == null || gA.Length > 3) throw new SerializationException("Serialize error");

    foreach (var g in gA) Debug.Log(g);
}

public static void Save(Game[] gA) {
    using (var f = File.Create(Directory.GetCurrentDirectory() + "\\sample.bin")) new BinaryFormatter().Serialize(f, gA);
}

public static Game[] Load() {
    using (var f = File.Open(Directory.GetCurrentDirectory() + "\\sample.bin", FileMode.Open)) return (new BinaryFormatter().Deserialize(f)) as Game[];
}

//Stub class used for testing purposes
[Serializable]
class Game {
    public int propertyStub { get; set; }

    public override string ToString() {
        return "propertyStub value: " + propertyStub;
    }
}

In addition, it's worth mentioning that the .net Binary serializer is pretty slow in comparison to other common serialization methods in general usage cases, so I advise against using it in your final builds. Other well-known serialization methods like XML and JSON generate very human readable output, and works well when you don't care about final file size, or serialization speed. If you really want speed and size, the Google Protocol Buffers (Protobuf) are a good starting point.

All of the links I've given you contain boiler-plate copy and paste code, although they all end up serializing dynamic array lists. As I said above, simply changing the explicit casts to arrays should not cause you any headache.

Upvotes: 3

Related Questions