Hello, I know there are examples of how to achieve what I am asking, but they are very difficult for me to understand as I am new to serialization. So I am creating an rts style game, I have buildings that I need to place in game and also ai characters. This means I need to be able to find there position, health, stats, is they are set active, null, so on. However I have no idea how to achieve this with game objects. Does anyone know how to achieve this? my current working script is :
using UnityEngine;
using System.Collections;
using System;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class Game_SaveLoad : MonoBehaviour {
private GameObject pause;
private World_Camera myWorld_Camera;
void Start()
{
myWorld_Camera = Camera.main.GetComponent<World_Camera>();
}
public void Save()
{
BinaryFormatter binary = new BinaryFormatter();
FileStream fStream = File.Create(Application.persistentDataPath + "/saveFile.brain");
SaveManager saver = new SaveManager();
saver.time = World_Calendar.calendarS.totalGameSeconds;
saver.fame = World_Fame.fameS.currentFame;
saver.resourceCash = Resource_Inventory.inventoryS.currentResourceCash;
saver.resourceGold = Resource_Inventory.inventoryS.currentResourceGold;
saver.resourceEars = Resource_Inventory.inventoryS.currentResourceEars;
binary.Serialize(fStream, saver);
fStream.Close();
}
public void Load()
{
if(File.Exists(Application.persistentDataPath + "/saveFile.brain"))
{
BinaryFormatter binary = new BinaryFormatter();
FileStream fStream = File.Open(Application.persistentDataPath + "/saveFile.brain", FileMode.Open);
SaveManager saver = (SaveManager)binary.Deserialize(fStream);
fStream.Close();
World_Calendar.calendarS.totalGameSeconds = saver.time;
World_Fame.fameS.currentFame = saver.fame;
Resource_Inventory.inventoryS.currentResourceCash = saver.resourceCash;
Resource_Inventory.inventoryS.currentResourceGold = saver.resourceGold;
Resource_Inventory.inventoryS.currentResourceEars = saver.resourceEars;
pause = GameObject.Find("PauseMenu");
pause.SetActive(false);
Time.timeScale = 1f;
myWorld_Camera.enabled = true;
}
}
}
[Serializable]
public class SaveManager
{
public double time;
public int fame;
public int resourceCash;
public int resourceGold;
public int resourceEars;
}
Also how to I access this script from another scene? Like I said, new to serialization, sorry if anything is obvious.
Simple way is to have one intermediate serializable class that has void FillFrom(GameObject) method which fills its variables from building(or unit or whatever) and GameObject Restore() method which instantiates object(from prefab for example) of needed type(which should also be saved/loaded as variable) and restores all of its values.
To find all buildings/units/whatever best way to do is tags. Make sure all buildings have same tag like “Building” and then GameObject.FindGameObjectsWithTag and then save them.
Make object it’s attached to as DontDestroyOnLoad and then just Find that object on another scene.
sorry for being a pain but this whole serialization this is going over my head, could I have an example of what you mean by FillFrom? and the Restore()? this is the first time I’ve heard of them. I’m not using any instantiate objects, but the majority of the objects do have a prefab. I’ve heard about using ID’s but I haven’t found an example of how to apply or reference them for saving and loading.
Because they’re not in any manual, of course. That’s how I call methods that do things I need. You can name them anything you want.
Then how are you loading buildings? You can’t deserialize GameObject directly (unfortunately)…
And now for code of simple case:
Large code
using UnityEngine;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
//This class contains all variables we want to save and load and methods for actual remembering and restoring those variables
[Serializable]
public class BuildingSaver
{
//public are serialized by default so I like making them public here instead of marking each as serializable
//Basically there should be all variables you want to save from building
public Vector3 position;
public float health;
public string name;
//Stores GameObject representing building in this class
public void FillFrom(GameObject obj)
{
//We fill all variables we want to save from building supplied to us
position = obj.transform.position;
health = obj.GetComponent<Building>().health;
name = obj.name;
}
//Restores GameObject (building) we stored in this class
public GameObject Restore()
{
//We restore building into how it should be from data we stored here
//I create a new object here, but usually you'd want to Instantiate prefab and set its components values like health/position for them.
GameObject newObj = new GameObject(name);
newObj.tag = "Building";
newObj.transform.position = position;
Building build = newObj.AddComponent<Building>();
build.health = health;
return newObj;
}
}
//Just example of some script attached to every building that needs some/all of its variables saved
public class Building : MonoBehaviour
{
public float health;
}
//This class saves all buildings and loads all buildings when you call Save(...) or Load(...) with specified formatter (like BinaryFormatter) and Stream (like FileStream).
public class Saver : MonoBehaviour
{
public void Save(Formatter format, Stream stream)
{
//Find all objects by tag "Building". Or use other way of getting all buildings.
GameObject[] buildings = GameObject.FindGameObjectsWithTag("Building");
//List we will actually write to file.
List<BuildingSaver> saveBuildings = new List<BuildingSaver>();
//We store each building in type of data that can be serialized/deserialized and store it in list
for(int a=0;a<buildings.Length;a++)
{
BuildingSaver saver = new BuildingSaver();
saver.FillFrom(buildings[a]);
saveBuildings.Add(saver);
}
//And we write that list.
format.Serialize(stream, saveBuildings);
}
public void Load(Formatter format, Stream stream)
{
//We restore list of buildings from stream. Note: format must be same type as used by Save(...) or it will go error.
List<BuildingSaver> saveBuildings = (List<BuildingSaver>)format.Deserialize(stream);
//And we restore each building
for(int a=0;a<saveBuildings.Count;a++)
{
GameObject building = saveBuildings[a].Restore();
//Maybe do something with building? Like changing parent or setting up something or such.
}
}
}
Note: it doesn’t save rotation, scale, any other variables, etc - that you decide for yourself if you need to save them and write it.
What I proposed might not be best way, but it’s simplest as I think and should work.
Thank you for the advice and help, I get what you mean now with the FillFrom after messing around, sorry also a bit tired. I will give this all a look when I get some sleep, as I’m useless currently, and report back.
I do not know if the OP got help out of this but it certainly helped me. Thank you so much, Teravisor. Your sample code helped me out a lot! I had to finagle with it a lot but it was a great help.