Advertisement
  1. Game Development
  2. Game Engine Development

Como Guardar y Cargar el progreso del jugador en Unity

Scroll to top
Read Time: 8 min

() translation by (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

En este tutorial vas a aprender a desarrollar un sistema de creación y gestión de partidas guardadas para tus juegos en Unity.  Vamos a construir un sistema con un menú principal que permita al jugador guardar y cargar una única partida. Los conceptos desarrollados te permitirán crear un sistema de guardado que se adapte a tu videojuego.

Al finalices el tutorial, habrás aprendido:

  • guardar y cargar datos del juego en Unity3D usando serialización
  • utilizar variables estáticas para conservar los datos cuando cambiemos de escena

Nota: Este método para guardar y cargar datos de juego funciona en todas las plataformas excepto en el reproductor Web. Para mas información sobre guardar datos de juego en el reproductor Web, puedes echarle un vistazo a la documentación oficial en Unity Web Player y comunicación con el navegador.

Vamos a Serializar

Lo primero que vamos a hacer es crear un código que permite serializar los datos del juego — es decir, convertirlo a un formato que puede ser salvado y cargado más adelante. Para ello, vamos a crear un script en C# que llamaremos SaveLoad Este script gestionará de todas las funcionalidades de guardar y cargar.

Referenciaremos este script desde otros scripts, así que vamos a hacer una clase estática mediante la adición de la palabra static entre public y class. Borraremos la parte : MonoBehaviour , ya que no vamos a usar el script en un GameObject. Y puesto que ya no hereda de MonoBehaviour, vamos a eliminar las funciones Start y Update.

El código resultante debe tener este aspecto:

1
using UnityEngine;
2
using System.Collections;
3
4
public static class SaveLoad {
5
6
}

Ahora, añadiremos algunas nuevas funcionalidades a este script, justo debajo de donde dice using System.Collections; añadimos lo siguiente:

1
using System.Collections.Generic; 
2
using System.Runtime.Serialization.Formatters.Binary; 
3
using System.IO;

La primera línea nos permite utilizar listas dinámicas en C#, pero esto no es necesario para la serialización. La segunda línea es lo que nos permite usar las capacidades de serialización del sistema operativo dentro del script. En la tercera línea, IO significa Entrada/Salida y es lo que nos permite escribir y leer desde nuestro ordenador o dispositivo móvil. En otras palabras, esta línea nos permite crear archivos y poder leerlos más tarde.

¡ Ahora estamos listos para serializar los datos!

Creando Clases Serializables

Ahora que nuestro script tiene la capacidad de serializar, vamos a tener que configurar algunas clases para que se puedan serializar. Si piensas en un juego de rol básico, como Final Fantasy, ofrece a los jugadores la habilidad de crear y cargar partidas guardadas diferentes. Así que vamos a  crear un nuevo script de C# llamado Game y definir unas variables para guardar tres objetos: un caballero (knight), un pícaro(rogue) y un mago(wizard). Cambia el código del script para parecerse a esto:

1
using UnityEngine;
2
using System.Collections;
3
4
[System.Serializable]
5
public class Game { 
6
7
    public static Game current;
8
    public Character knight;
9
    public Character rogue;
10
    public Character wizard;
11
12
    public Game () {
13
        knight = new Character();
14
        rogue = new Character();
15
        wizard = new Character();
16
    }
17
        
18
}

La línea [System.Serializable] dice a Unity que este script se puede serializar — en otras palabras, que podemos guardar todas las variables de este script. ¡ Genial! Según la documentación oficial, Unity puede serializar los tipos siguientes:

  • Todos los tipos de datos básicos (como int, string, float, bool).
  • Algunos tipos integrados (incluyendo Vector2, Vector3, Vector4, cuaternión, Matrix4x4, Color, Rect y LayerMask).
  • Todas las clases heredan de UnityEngine.Object (incluyendo GameObject, Component, MonoBehavior, Texture2D y AnimationClip).
  • Enums.
  • Arrays y listas de un tipo serializable.

La primera variable, current, es una referencia estática a una instancia de Game. Cuando guardamos o cargamos un juego, vamos a establecer esta variable estática a esa instancia del juego en particular por lo que podemos hacer referencia a la "partida actual" desde cualquier parte del proyecto. Mediante el uso de funciones y variables estáticas, no tenemos que usar la funcion GetComponent()  de un gameObject. Práctico!

¿Has visto que se está haciendo referencia a algo llamado Character? Aún no lo hemos creado, así que vamos a crear un nuevo script que llamaremos Character para definir esta clase.

1
using UnityEngine;
2
using System.Collections;
3
4
[System.Serializable] 
5
public class Character {
6
7
    public string name;
8
9
    public Character () {
10
        this.name = "";
11
    }
12
}

Te estarás preguntando por qué necesitábamos una nueva clase si sólo estamos almacenando una variable string. De hecho, sólo tendríamos que  reemplazar Character por string en el script Game  . Pero queremos mostrar la profundidad de estas estructuras: puedes guardar y cargar las clases que hacen referencia a otras clases y así sucesivamente, siempre que cada clase sea serializable.

Ahora que nuestras clases están configuradas para ser guardadas y cargadas, volvamos a nuestro script  SaveLoad para añadir la posibilidad de guardar partidas.

Guardando el estado del juego

Un menú "Load Game" por lo general muestra una lista de partidas guardadas, así que vamos a crear en savedGames. una lista List de tipo Game Crear una lista estática static List , de forma que sólo exista una lista de partidas guardadas en nuestro proyecto. El código debería parecerse a esto:

1
using UnityEngine;
2
using System.Collections;
3
using System.Collections.Generic;
4
using System.Runtime.Serialization.Formatters.Binary;
5
using System.IO;
6
7
public static class SaveLoad {
8
9
    public static List<Game> savedGames = new List<Game>();
10
11
}

A continuación, vamos a crear una nueva función estática para guardar el juego:

1
    public static void Save() {
2
        savedGames.Add(Game.current);
3
        BinaryFormatter bf = new BinaryFormatter();
4
        FileStream file = File.Create (Application.persistentDataPath + "/savedGames.gd");
5
        bf.Serialize(file, SaveLoad.savedGames);
6
        file.Close();
7
    }    

La línea 2 agrega nuestro juego actual a la lista de partidas guardadas. Esa lista es lo que vamos a serializar. Para ello, primero necesitamos crear un nuevo BinaryFormatter, que gestionará el trabajo de serialización. Esto es lo que hace la línea 3 .

En la línea 4, estamos creando un FileStream, que es esencialmente un puntero al archivo para poder enviar los datos. Utilizamos File.Create() para crear un archivo en la ruta que pasamos como parámetro. Convenientemente, Unity cuenta con una ruta por defecto para almacenar los archivos del juego (esta ruta se actualiza según la plataforma para la que generemos el juego) que podemos referenciar con Application.persistentDataPath.

Como estamos creando un archivo nuevo, no podemos decir solo donde está el archivo, también tenemosque indicar el nombre del archivo. Hay dos partes a este archivo:

  1. el nombre del archivo
  2. el tipo de archivo

Usaremos savedGames para el nombre del archivo, y vamos a utilizar un tipo personalizado gd (de "game data")  para el tipo de archivo. El resultado es un archivo del juego llamado savedGames.gd en la ruta Application.persistentDataPath. (Más adelante, puedes guardar otros tipos de cosas con este tipo, por ejemplo, se puede guardar configuración de opciones de los usuarios como options.gd).

Nota: Puede crear el  tipo  de archivo que quieras. Por ejemplo, la serie Elder Scrolls utiliza .esm como su tipo de archivo. Se puede crear por ejemplo savedGames.QuePinQuePan

En la línea 5, estamos llamando a la funcionalidad Serialize de BinaryFormatter para guardar la lista savedGames en nuestro nuevo archivo. Después de eso, en la Linea 6, tenemos que cerrar el archivo que hemos creado.

Badda bing, badda boom. Nuestros juegos se han guardado.

Cargando el estado del juego.

En la función Save, serializa la lista de partidas guardadas en un lugar específico. Por el contrario, el código para cargar nuestros juegos debería tener este aspecto:

1
    public static void Load() {
2
        if(File.Exists(Application.persistentDataPath + "/savedGames.gd")) {
3
            BinaryFormatter bf = new BinaryFormatter();
4
            FileStream file = File.Open(Application.persistentDataPath + "/savedGames.gd", FileMode.Open);
5
            SaveLoad.savedGames = (List<Game>)bf.Deserialize(file);
6
            file.Close();
7
        }
8
    }

En la línea 2, comprobamos si existe un archivo de juego guardado. (Si no es así, no tenemos nada que cargar, obviamente.) En la línea 3, creamos un BinaryFormatter igual que hicimos en la función Save. En la línea 4, creamos un FileStream, pero esta vez, para leer los datos desde el archivo. Utilizamos File.Open indicando la ruta Application.persistentDataPath y el nombre del archivo savedGames.gd

La línea 5 es un poco densa, así que vamos a descomprimirlo:

  • bf. Deserialize(File) busca el archivo en la ubicación que hemos especificados anteriormente y lo deserializa.
  • No podemos simplemente enviar binarios en Unity y esperar que funcione, sin embargo,podemos convertir (o castear) nuestro archivo deserializado al tipo de datos que queremos que sea, que en este caso es una lista (List) del tipo Game.
  • Luego ponemos la lista como nuestra lista de partidas guardadas.

Por último, en la línea 6, cerramos el archivo de la misma manera que hicimos en la función de guardar.

Nota: El tipo de datos a los que convertimos los datos deserializados puede cambiar dependiendo de lo que se necesite en cada momento. Por ejemplo, Player.lives = (int) bf. Deserialize(File);.

Conclusión

Nuestro script SaveLoad ya está termiando y debe tener este aspecto:

1
using UnityEngine;
2
using System.Collections;
3
using System.Collections.Generic;
4
using System.Runtime.Serialization.Formatters.Binary;
5
using System.IO;
6
7
public static class SaveLoad {
8
9
    public static List<Game> savedGames = new List<Game>();
10
  		
11
	//it's static so we can call it from anywhere

12
	public static void Save() {
13
		SaveLoad.savedGames.Add(Game.current);
14
		BinaryFormatter bf = new BinaryFormatter();
15
		//Application.persistentDataPath is a string, so if you wanted you can put that into debug.log if you want to know where save games are located

16
		FileStream file = File.Create (Application.persistentDataPath + "/savedGames.gd"); //you can call it anything you want

17
		bf.Serialize(file, SaveLoad.savedGames);
18
		file.Close();
19
	}	
20
	
21
	public static void Load() {
22
		if(File.Exists(Application.persistentDataPath + "/savedGames.gd")) {
23
			BinaryFormatter bf = new BinaryFormatter();
24
			FileStream file = File.Open(Application.persistentDataPath + "/savedGames.gd", FileMode.Open);
25
			SaveLoad.savedGames = (List<Game>)bf.Deserialize(file);
26
			file.Close();
27
		}
28
	}
29
}

Estos son los fundamentos de cómo guardar y cargar en Unity. En el archivo de proyecto adjunto, encontrarás algunas otras escrituras que muestran cómo gestionar llamadas a estas funciones y cómo visualizar los datos mediante la GUI de Unity.

¡Sé el primero en conocer las nuevas traducciones–sigue @tutsplus_es en Twitter!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Game Development tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.