Reputation: 27
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
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