Jump to content
kaito

Save Load del juego

Recommended Posts

Enlace de descarga: https://app.box.com/s/384vkwhfosk9zgzpftphq3oi35yifhpb

 

 

 

Para este sistema de Save/Load necesitamos crear un prefab de la entidad. El prefab se necesita para instanciar/crear un nuevo GameObject de la forma más sencilla.

El sistema de guardado consta de una clase estática llamada GameData que almacena una sola instancia MonoBehaviour(script) de cada GameObject dentro de una ArrayList, y dispone de las funciones Save y Load:

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;
		
	}

}

 

Uso la instancia de un script como referencia del GameObject, y no una clase serializada directamente, porque es constante durante todo el tiempo de juego.

Tampoco utilizo el GameObject directamente como referencia, para que este sistema de guardado pueda ser utilizado por cualquier tipo de script(SOLO UN SCRIPT por GameObject podrá implementar el sistema de guardado).

Este único script recoge los datos de los componentes y otros script del GameObject.

Para poder guardar los datos del juego de forma generalizada, utilizo una clase base serializada llamada EntityData con el campo ResourcesPath y la funcion OnLoad:

[System.Serializable]
public class EntityData{

    public string ResourcesPath;
    public virtual void OnLoad(GameObject gameObject){}

}

 

Y además, necesito una interfaz para poder extraer los datos de los GameObjects, con la funcion GetData():

interface ISaveLoad{

	object GetData();

}

 

UTILIZACION

 

Como he dicho, antes de nada, es necesario crear un prefab de la entidad/GameObject que alojaremos en la carpeta Resources.

A continuación, dentro de algún script del GameObject a guardar(solo un script del GameObject puede implementar este sistema):

1) Creamos una clase derivada de EntityData. Esta clase hereda ResourcesPath y la funcion OnLoad necesaria para la carga de la escena, y añadiremos los datos que queramos guardar del GameObject. Por ejemplo:

[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)
		{
			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>();
			//Transformamos el object en la clase de datos de la entidad
			script.datos = (ScriptEntidad1.Data) this;

		}

	}

	public Data datos;

 

He creado una clase llamada Data, derivada de EntityData, donde almaceno los datos del Transform y dos campos de vida y experiencia. En la funcion OnLoad asigno estos valores a los componentes, es decir, al Transform y al Script al que he vinculado los datos, en este caso al script 'ScriptEntidad1'.

2) Añadimos el script a la lista de GameData desde la funcion Start() de Unity, y los eliminamos al destruir el GameObject desde la funcion OnDestroy() de Unity:

	void Start () {
		
		//Añadir GameObject a la lista
		GameData.Data.Add(this);
		
	}

	void OnDestroy(){

		//Eliminar GameObject de la lista
		GameData.Data.Remove(this);

	}

 

3) Añardir la funcion GetData de la interfaz ISaveLoad:

public class ScriptEntidad1 : MonoBehaviour, ISaveLoad {


	object ISaveLoad.GetData(){
		
		//Actualizar Datos Transform
		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;
		
	}


}

 

Si quereis ver el codigo completo del 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)
		{
			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>();
			//Transformamos el object en la clase de datos de la entidad
			script.datos = (ScriptEntidad1.Data) this;

		}

	}

	public Data datos;
	
	object ISaveLoad.GetData(){
		
		//Actualizar Datos Transform
		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 () {
		
		//Añadir GameObject a la lista
		GameData.Data.Add(this);
		
	}

	void OnDestroy(){

		//Eliminar GameObject de la lista
		GameData.Data.Remove(this);

	}

}

 

 

 

 

 

Edited by kaito

Share this post


Link to post
Share on other sites

Por si alguien tiene dificultades con la descarga:

En el foro inglés también tenían problemas con la descarga desde box.

He subido el ejemplo al foro, para solucionar este problema.

Enlace de descarga:  SaveLoad.unitypackage

Edited by kaito

Share this post


Link to post
Share on other sites

UnitySpain © Todos los derechos reservados 2020
×
×
  • Create New...