Download link.
For this Save / Load system need to create a prefab of the entity previously. The prefab is required to instantiate a new GameObject in a simpler way.
The system consists of a static class called GameData that stores only one MonoBehaviour(script) instance of eachGameObject in a ArrayList, and has the Save and Load functions.
using UnityEngine;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public static class GameData {
public static string file = "GameData.dat";
public static ArrayList Data = new ArrayList(); //Aqui se almacenaria todos los datos como objetos
public static bool Save(){
FileStream fs = new FileStream(file, FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
ArrayList arrayList = new ArrayList();
foreach(object data in Data){
ISaveLoad script = (ISaveLoad) data;
arrayList.Add(script.GetData());
}
try
{
formatter.Serialize(fs, arrayList);
}
catch (System.Runtime.Serialization.SerializationException e)
{
UnityEngine.Debug.Log("Failed to serialize. Reason: " + e.Message);
return false;
}
finally
{
fs.Close();
}
arrayList.Clear();
arrayList = null;
formatter = null;
fs = null;
return true;
}
public static bool Load(){
if(!File.Exists(file)) return false;
FileStream fs = new FileStream(file, FileMode.Open);
ArrayList arrayList;
try
{
BinaryFormatter formatter = new BinaryFormatter();
arrayList = (ArrayList) formatter.Deserialize(fs);
formatter = null;
}
catch (System.Runtime.Serialization.SerializationException e)
{
UnityEngine.Debug.Log("Failed to deserialize. Reason: " + e.Message);
return false;
}
finally
{
fs.Close();
}
Data.Clear();
//Instanciar GameObjects a escena
foreach(object data in arrayList){
EntityData entityData = (EntityData)data;
entityData.OnLoad(UnityEngine.Object.Instantiate<GameObject>(Resources.Load<GameObject>(entityData.ResourcesPath)));
}
arrayList.Clear();
arrayList = null;
fs = null;
return true;
}
}
Use instance of script as reference of GameObject, and not serialized data class, because is constant in the Game time.
Also, the system does not uses a GameObject as reference directly, because this system can be used by any script of GameObject (ONLY ONE SCRIPT per GameObject)
This unique script collects data from components and others scripts of GameObject.
To save game data widely, I use a serialized base class called EntityData with ResourcesPath field and OnLoad function:
[System.Serializable]
public class EntityData{
public string ResourcesPath;
public virtual void OnLoad(GameObject gameObject){}
}
And besides, I need an interface to extract data from the GameObjects, with GetData () function, from any script:
interface ISaveLoad{
object GetData();
}
USAGE
As I said, first of all, you need to create a prefab of GameObject to place in the Resources folder.
Then, in only one script of GameObject:
- Create a class derived from EntityData. This class inherits ResourcesPath and OnLoad function required for loading the GameObject in the scene(Assign data to components). For example:
[System.Serializable]
public class Data : EntityData{
[HideInInspector]
public float px,py,pz,rx,ry,rz,sx,sy,sz;
public int vida = 100,
experiencia = 0;
public override void OnLoad (GameObject gameObject)
{
//Assign Transform data
Transform transform = gameObject.GetComponent<Transform>();
transform.position = new Vector3(px,py,pz);
transform.eulerAngles = new Vector3(rx,ry,rz);
transform.localScale = new Vector3(sx,sy,sz);
ScriptEntidad1 script = gameObject.GetComponent<ScriptEntidad1>();
//Convert the object in the data class of new instantiated GameObject
script.datos = (ScriptEntidad1.Data) this;
}
}
public Data datos;
- Add the instance of script to the list of GameData from Start () function of Unity, and remove it when the GameObject is destroyed from OnDestroy () function of Unity:
void Start () {
GameData.Data.Add(this);
}
void OnDestroy(){
GameData.Data.Remove(this);
}
- Add the GetData function of ISaveLoad interface:
public class ScriptEntidad1 : MonoBehaviour, ISaveLoad {
object ISaveLoad.GetData(){
//Update Transform data
Vector3 temp = this.transform.position;
this.datos.px = temp.x;
this.datos.py = temp.y;
this.datos.pz = temp.z;
temp = this.transform.eulerAngles;
this.datos.rx = temp.x;
this.datos.ry = temp.y;
this.datos.rz = temp.z;
temp = this.transform.localScale;
this.datos.sx = temp.x;
this.datos.sy = temp.y;
this.datos.sz = temp.z;
return this.datos;
}
}
If you want to see the complete code for the script:
using UnityEngine;
using System.Collections;
public class ScriptEntidad1 : MonoBehaviour, ISaveLoad {
[System.Serializable]
public class Data : EntityData{
[HideInInspector]
public float px,py,pz,rx,ry,rz,sx,sy,sz;
public int vida = 100,
experiencia = 0;
public override void OnLoad (GameObject gameObject)
{
//Assign Transform data
Transform transform = gameObject.GetComponent<Transform>();
transform.position = new Vector3(px,py,pz);
transform.eulerAngles = new Vector3(rx,ry,rz);
transform.localScale = new Vector3(sx,sy,sz);
ScriptEntidad1 script = gameObject.GetComponent<ScriptEntidad1>();
//Convert the object in the data class of new instantiated GameObject
script.datos = (ScriptEntidad1.Data) this;
}
}
public Data datos;
object ISaveLoad.GetData(){
//Update Transform data
Vector3 temp = this.transform.position;
this.datos.px = temp.x;
this.datos.py = temp.y;
this.datos.pz = temp.z;
temp = this.transform.eulerAngles;
this.datos.rx = temp.x;
this.datos.ry = temp.y;
this.datos.rz = temp.z;
temp = this.transform.localScale;
this.datos.sx = temp.x;
this.datos.sy = temp.y;
this.datos.sz = temp.z;
return this.datos;
}
void Start () {
GameData.Data.Add(this);
}
void OnDestroy(){
GameData.Data.Remove(this);
}
}