Hola, he estado investigando como hacer para guardar los cambios en mi aplicación y cargarlos cuando vuelva a abrirla, lo único que necesito almacenar es las posiciones de las instancias, ademas de su color y nombre; y la rotación del objeto principal. ¿Como podría hacerlo?
Mediante PlayerPrefs o serializar con json.
Si ya, he visto que esos son los dos métodos, pero lo que no tengo claro es como tendría que hacer para guardar las variables y eso...
o en un txt con streamreader y streamwriter...
Cita de: Auronity date=1507648875¿Como podría hacerlo?
siempre que seas capaz de traducir lo que desees de la escena (posiciones, rotaciones y/o color de determinados objetos, como decis) a un formato cualquiera(xml, json, binario en general, txt, etc), y que luego seas capaz de leer dicho formato para cargar la info en la escena... listo, en eso consta guardar y cargar. incluso con un txt podes hacerlo. El dilema que viene luego es como está organizado dicho formato.
Yo conozco tres formas:
Si usas algo conocido (xml, JSON, playerPrefs) debes hacer las cosas como ellos dicen y tenes sus vulnerabilidades, JSON es muy conocido y comodo, unity lo soporta bien, guardar vectores, quaternions y colores es pan comido, pero, tambien es pan comido para mi meterme en los json y toquetear todo. igual es un formato muy lindo y amigable
Si usas algo propio, (un binario o texto ".Auronity"),tenes la ventaja de que vos elegis como organizarte a la hora de todo, claro que luego debes construir tu propio parser. Podes meter algoritmos para enmascarar cada vez mas la info, podes incluir headers que tengan info de como decodificar todo, y asi, y asi.... Es muy util para practicarlo.
Usar el formateador binario de Unity(en si de System), agarras una clase serializible con los datos bien basicos de C# (int, string, float, long, double ,etc) y le decis armame el binario de esta clase que me intesa y guardalo en tal lugar. Unity te va a hacer un chorizo de binario que nadie entiende solo Unity, y luego obviamente lo podes leer.
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Pero tu problema no es esto, el problema que vas a tener cuando cargas tal informacion es, por ejemplo, tal posicion, a que objeto le corresponde? aca es donde tenes que ver como identificas cada objeto primero, entonces la posicion/rot o color ahora tiene un ID, que esta asociado a un objeto en particular, entonces lo que antes guardabas quizas era un vector3+Color+Quaternion...ahora es una estructura o clase generica con dos o mas datos:
class Data
{
int id;
Vector3 pos;
Quaternion rot;
Color color;
}
Entonces ahora no guardas colores, ni pos, etc... ahora guardas varios de estos "Data" , quizas una List<Data>. El id te sirve para identificar el objeto en escena, y el resto es informacion a cargar a dicho objeto. Esto es solo un ejemplo, podes hacerlo de la forma que quieras, por eso el énfasis en
como te organizas, y no en como guardar/cargar.
no olvides encriptar los datos para que no te hackeen el puntaje del juego o items
La aplicación es solo para uso personal así que la seguridad me da igual xD. Solo quiero saber cual es el método mas simple, ya que solo llevo un mes programando en C# y usando Unity
Cita de: Auronity date=1507660499La aplicación es solo para uso personal así que la seguridad me da igual xD. Solo quiero saber cual es el método mas simple, ya que solo llevo un mes programando en C# y usando Unity
Da igual cuanto lleves, metodo mas simple que otro no los hay, en todos tenes que aprender de 0, en algunos tenes mas que aprender que otro, te recomiendo que te auto exijas, en la programacion hay muchos caminos, eleji los menos costosos, pero no por ello mas faciles!
Lee sobre JSON y como encriptarlo, es lo mas "facil" para empezar
Abrazo!
Aver si me aclaro, porque cuanto mas investigo mas me pierdo... Suponiendo que mi objetivo es guardar la rotación de un gameObject para poder cargarla cuando vuelva a abrir la aplicación, he visto que lo que tengo que hacer es crear un script donde se guarda el Quaternion serializable, y otro script que controla todo el proceso de guardar y cargar. Supongo que el script que guarda la rotacion del objeto debera ser un componente de ese objeto, corrijanme si me equivoco.
Ahora adjunto el script a ver si voy bien o que xD
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Xml.Serialization;
[System.Serializable]
public class Mapa : MonoBehaviour {
public Quaternion rotacion;
}
El otro script lo tengo en blanco porque ese si que no entiendo nada. Espero que me puedan ayudar a ir paso a paso
Gracias
A ver, no te líes, lo mas simple suele ser lo mejor casi siempre.
Tienes dos opciones, dependiendo de si necesitas proteger la información o no, como ya has dicho que no necesitas protegerla puedes usar el PlayerPrefs, que es mucho mas simple que la serialización, sobre todo porque dices que llevas poco y te veo un poco perdido.
El PlayerPrefs tiene la desventaja de que los datos son accesibles y no es lo mas elegante pero ya hemos visto que eso no te importa, en realidad esta pensado para guardar preferencias del jugador, por ejemplo si el sonido está activado en el juego o no y cosas de esas, pero en realidad podemos guardar lo que queramos.
Hay tres funciones para guardar y tres funciones para cargar información:
PlayerPrefs.SetFloat("NombreDelDato",valorFlotante);
PlayerPrefs.SetInt("NombreDelDato",valorEntero);
PlayerPrefs.SetString("NombreDelDato",valorString);
PlayerPrefs.GetFloat("NombreDelDato");
PlayerPrefs.GetInt("NombreDelDato");
PlayerPrefs.GetString("NombreDelDato");
Que sirven para cargar y guardar datos de tipo Int, String y Float
Lo que te interesa es guardar la información del transform.position y el transform.rotation, que son valores de Tipo Vector3 y Quaternion que estan compuestos por 3 y 4 valores float.
Pues cogemos la información y la guardamos en el PlayerPrefs:
//Declaramos las variables de la posicion y la rotacion.
float posicionX, posicionY, posicionZ;
float rotacionX, rotacionY, rotacionZ, rotacionW;
// Cogemos los valores de la posicion del transform del objeto
posicionX = objeto.transform.position.x;
posicionY = objeto.transform.position.y;
posicionZ = objeto.transform.postiion.z;
// Guardamos esos valores en el PlayerPrefs
PlayerPrefs.SetFloat("PosicionX", posicionX);
PlayerPrefs.SetFloat("PosicionY", posicionY);
PlayerPrefs.SetFloat("PosicionY", posicionY);
// Ahora con la rotacion.
rotacionX = objeto.transform.rotation.x;
rotacionY = objeto.transform.rotation.y;
rotacionZ = objeto.transform.rotation.z;
rotacionW = objeto.transform.rotation.w;
// Guaradamos la rotacion en el PlayerPrefs
PlayerPrefs.SetFloat("RotacionX", rotacionX);
PlayerPrefs.SetFloat("RotacionY", rotacionY);
PlayerPrefs.SetFloat("RotacionZ", rotacionZ);
PlayerPrefs.SetFloat("RotacionW", rotacionW);
Con esto lo tenemos guardado, dependiendo del sistema operativo que uses se guarda en un sitio o en otro en windows se guarda en el registro, en MAC en la carpeta Library/Preferences, en iOS en el directorio Preferences en un archivo con el nombre del desarrollador.plist, en Android en data/nombredelpaquete/shared_prefs y en el resto de sistemas no tengo ni pajolera idea.
La carga es idéntica al guardado: creas las variables y lees la información desde el playerPrefs (solo lo hago con una a modo de ejemplo que si no voy a acabar escribiendo un libro):
float posicionX;
posicionX = PlayerPrefs.GetFloat("PosicionX");
Cuando las tengas cargadas las metes en el position o en el rotation usando un Vector3 o un Quaternion;
Verctor3 posicionObjeto;
// Aqui leemos los valores guaradados y los metemos en las variables
// Despues de leidos los guardamos en el Vector3
posicionObjeto = new Vector3 (posicionX, posicionY, posicionZ);
// Y asignamos el Vector3 de la posicion al position del transform.
objeto.transform.position = posicionObjeto;
Y lo mismo con el rotation, pero con un Quaternion en vez de un vector3.
El código esta hecho de cabeza sobre la marcha y desde luego no esta probado asi que he podido meter la pata en algo, pero mi intención es que captes la idea.
El PlayerPrefs tiene mas funciones, puedes comprobar si se ha guardado un dato, forzar que los datos se escriban en el disco (por ejemplo antes de salir del juego), borrar datos, o incluso vaciar todo el contenido del PlayerPrefs y cosas así, échale un vistazo a la API porque para almacenar poca información es bastante útil.
Por eso nunca descargo proyectos hechos por novatos, o gente con poco conocimiento... Alguien me puede explicar por que usáis playerprefs como si fuese bueno?... Esta clase estática de la librería de UnityEngine guarda en los registros del sistema target, no tiene que ser usada para guardar datos de partidas de un juego... Imaginaros que gta guardase la partida en el registro de windows... Pues es lo que hacéis con plauerprefs... Llenar los registros de mierda. No se por que no leeis la documentación. Volviendo al hilo, JSON o XML son sencillos si no tienes idea de parsear información, si buscas que alguien te de código lo tienes chungo... Suerte
Cita de: kingtrase date=1507858520No se por que no leeis la documentación
Por supuesto que lo sabés ... la flojera siempre gana.
Cita de: kingtrase date=1507858520Alguien me puede explicar por que usáis playerprefs como si fuese bueno?
Yo por eso no lo incluí en la lista de posibilidades, pienso que debería ser erradicado de este mundo, su nombre tan noob me da náuseas, su existencia es como un virus que avanza y no se detiene, su utilidad, cercana a nula, si seguimos así nuestros hijos...que digo?... nuestros nietos lo van a usar para guardar sus datos! maldito seas PlayerPrefs! maldito!
Cita de: lightbug date=1507863613 Por supuesto que lo sabés ... la flojera siempre gana.
Yo por eso no lo incluí en la lista de posibilidades, pienso que debería ser erradicado de este mundo, su nombre tan noob me da náuseas, su existencia es como un virus que avanza y no se detiene, su utilidad, cercana a nula, si seguimos así nuestros hijos...que digo?... nuestros nietos lo van a usar para guardar sus datos! maldito seas PlayerPrefs! maldito!
Jajajajaja sabes que no lo decía por ti amigo mio, tu comentario me hizo el día jajajaja
Muchas gracias gZone, he probado el código tal y como esta no tiene errores, el problema es que he querido separar la parte en que se guarda y carga los datos, metiendolas en un if (input.GetKeyDown... etc, dentro del Update, pero me da error en las siguientes palabras:
void Update () {
// Guarda la info al archivo "miData" y la envía a la consola
if (Input.GetKeyDown (KeyCode.S)) {
listado[1] = new miData (transform.rotation);
Guarda (archivo, listado);
print ("guardado en archivo " + listado [1].rotacion ().ToString ());
}
// Carga la info desde el archivo y la envía a la consola
if (Input.GetKeyDown (KeyCode.L)) {
listado = Carga (archivo);
if (listado == null)
print ("error al cargar");
else
print ("cargado desde archivo " + listado [1].rotacion ().ToString ());
}
}
*He usado solo la rotación y omitido la posición
habia pegado el codigo sin usar "code" para poner en rojo las palabras que me dan error, pero bueno... Las palabras que me dan error son: listado, archivo, rotacion y ToString
Perfecto, ya no da error. Lo he probado y parece que funciona, pulso S y guarda la rotación, lo muevo en un eje y guardo, y la rotación solo ha variado en ese eje. Hasta ahi todo bien. pero cuando pulso L para cargar solo me sale el mensaje en la consola pero el objeto no vuelve a la ultima posición guardada
Editado: ademas tampoco entiendo como funciona la parte de guardar, aunque he comprobado que funciona
Funciona!!! Muchas gracias por la ayuda. Se supone que esto funcionara igual al cerrar la aplicacion y volverla a abrir, se cargara la posicion?
Editado: vale, acabo de comprobar que si, puedo seguir el mismo proceso para guardar la posicion de las instancias?
Cita de: kingtrase date=1507858520Por eso nunca descargo proyectos hechos por novatos, o gente con poco conocimiento... Alguien me puede explicar por que usáis playerprefs como si fuese bueno?... Esta clase estática de la librería de UnityEngine guarda en los registros del sistema target, no tiene que ser usada para guardar datos de partidas de un juego... Imaginaros que gta guardase la partida en el registro de windows... Pues es lo que hacéis con plauerprefs... Llenar los registros de mierda. No se por que no leeis la documentación. Volviendo al hilo, JSON o XML son sencillos si no tienes idea de parsear información, si buscas que alguien te de código lo tienes chungo... Suerte
Como he dicho:
Tienes dos opciones, dependiendo de si necesitas proteger la información o no, como ya has dicho que no necesitas protegerla puedes usar el PlayerPrefs, que es mucho mas simple que la serialización, sobre todo porque dices que llevas poco y te veo un poco perdido.
El PlayerPrefs tiene la desventaja de que los datos son accesibles y no es lo mas elegante pero ya hemos visto que eso no te importa, en realidad esta pensado para guardar preferencias del jugador, por ejemplo si el sonido está activado en el juego o no y cosas de esas,
[/quote]
Esas y no otras son las causas, pero al fin y al cabo, el registro es un váter, así que lo de llenarlo de mierda no es mas que su destino
Seguro que cuando tenga muchos objetos hace un FindGameObjectWithTag en vez de crear hijos, o para invocar métodos de otros objetos un SendMessage, eso no es malo, es una forma de empezar. Según se van teniendo las cosas claras se va avanzando. Para mí, una de las cagadas mas grandes de Unity es tener todo público y pasarlo por referencia mediante el editor y está a la orden del día, una bonita forma de joder la encapsulación.
Vale, nueva duda (para variar), para que se entienda mejor explicare que mi app consiste en una esfera (mapa) sobre el que se pueden colocar marcadores del estilo de Google Street View. Roto la esfera con las flechas del teclado y los marcadores se mueven con ella porque se emparentan al instanciarse.
Respecto al tema de guardar/cargar ya puedo guardar la rotación de la "tierra" y la posición de los marcadores, también puedo cargar la rotación de la "tierra" pero cuando intento cargar la posición de los marcadores, es la esfera la que cambia de posición.
Entiendo el error pero no se como arreglarlo. Aquí dejo el script:
using UnityEngine;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Collections.Generic;
public class SaveLoad : MonoBehaviour {
//Para la Rotacion del mapa
dataR rotation;
string archivoR;
GameObject map;
//Para la posicion de los marcadores
GameObject[] listaMarcadores;
dataP position;
string archivoP;
[System.Serializable]
public class dataR {
// data almacenada en formato string serializable
private string rot;
// Quaternion a String
public dataR(Quaternion rotacion) {
rot = JsonUtility.ToJson(rotacion);
}
// recupera Quaternion rotación
public Quaternion rotacion() {
return JsonUtility.FromJson<Quaternion> (rot);
}
}
[System.Serializable]
public class dataP {
// data almacenada en formato string serializable
private string pos;
// Vector3 a String
public dataP(Vector3 posicion) {
pos = JsonUtility.ToJson(posicion);
}
// recupera Vector3 posición
public Vector3 posicion() {
return JsonUtility.FromJson<Vector3> (pos);
}
}
void Start () {
map = GameObject.Find ("hell mundo");
// un nombre de archivo
archivoR = "R_data";
archivoP = "P_data";
}
void Update () {
// Guarda la info al archivo "data" y la envía a la consola
if (Input.GetKeyDown (KeyCode.S)) {
listaMarcadores = GameObject.FindGameObjectsWithTag ("Marcador");
//Guardar rotacion del Mapa
rotation = new dataR (transform.rotation);
GuardaMapa (archivoR, rotation);
print ("guardado en archivo " + rotation.rotacion ().ToString ());
//Guardar posicion de los Marcadores
for(int i=0; i<listaMarcadores.Length; i++){
position = new dataP (listaMarcadores.transform.position);
GuardaMarca (archivoP, position);
print ("guardado en archivo " + position.posicion ().ToString ());
}
}
// Carga la info desde el archivo
if (Input.GetKeyDown (KeyCode.L)) {
//Cargar rotacion del Mapa
rotation = CargaMapa (archivoR);
if (rotation == null) {
print ("error al cargar");
} else {
map.transform.rotation = rotation.rotacion ();
print ("cargado desde archivo " + rotation.rotacion ().ToString ());
}
//Cargar posicion de los Marcadores
position = CargaMarca (archivoP);
if (position == null) {
print ("error al cargar");
} else {
map.transform.position = position.posicion ();
print ("cargado desde archivo " + position.posicion ().ToString ());
}
}
}
//Guardar rotacion del Mapa
void GuardaMapa(string nombreArchivo, dataR rotation) {
BinaryFormatter bufferData = new BinaryFormatter();
FileStream archivo = File.Create (Application.persistentDataPath + "/" + nombreArchivo);
bufferData.Serialize(archivo, rotation);
archivo.Close();
}
//Cargar rotacion del Mapa
dataR CargaMapa(string nombreArchivo) {
if (File.Exists (Application.persistentDataPath + "/" + nombreArchivo)) {
BinaryFormatter bufferData = new BinaryFormatter ();
FileStream archivo = File.Open (Application.persistentDataPath + "/" + nombreArchivo, FileMode.Open);
dataR salida = (dataR)bufferData.Deserialize (archivo);
archivo.Close ();
return salida;
} else {
return null;
}
}
//Guardar posicion de los Marcadores
void GuardaMarca(string nombreArchivo, dataP position) {
BinaryFormatter bufferData = new BinaryFormatter();
FileStream archivo = File.Create (Application.persistentDataPath + "/" + nombreArchivo);
bufferData.Serialize(archivo, position);
archivo.Close();
}
//Cargar posicion de los Marcadores
dataP CargaMarca(string nombreArchivo) {
if (File.Exists (Application.persistentDataPath + "/" + nombreArchivo)) {
BinaryFormatter bufferData = new BinaryFormatter ();
FileStream archivo = File.Open (Application.persistentDataPath + "/" + nombreArchivo, FileMode.Open);
dataP salida = (dataP)bufferData.Deserialize (archivo);
archivo.Close ();
return salida;
} else {
return null;
}
}
}
Es una adaptación del script de gZone.
PD: El script es un componente de la esfera.
Cita de: FNP date=1507985143Esas y no otras son las causas, pero al fin y al cabo, el registro es un váter, así que lo de llenarlo de mierda no es mas que su destino
No se lo que sabes sobre el Registro de windows... pero en el se guarda la configuración de tu ordenador, la de tu perfil de usuario, la información sobre programas, etc.
si crees que esos datos de carácter importante para el sistema están almacenados en un váter para que los gérmenes de vuestro juego se guarden en el pues pondré una vela cada noche por tu ordenador, pero te aseguro que yo prefiero mi váter limpio.
Sigo intentando implementar el guardado/cargado y he conseguido hacer que se guarden todas las instancias, durante las pruebas mantengo abierta la carpeta donde se guardan/cargan los datos para comprobarlo. Lo que me esta dando problemas ahora es el cargado, no se que linea estará mal en el código pero aun habiendo archivos en la carpeta, me devuelve null. Ayer conseguí que se cargaran todos los marcadores guardados menos el ultimo guardado, y cuando quise arreglarlo (cambiando una j del bucle for por j+1) solo conseguí que no encontrara ninguno. Aquí esta el script (solo la parte que me da problemas), Espero que si alguien ve el fallo me pueda ayudar a arreglarlo.
// Carga la info desde el archivo
if (Input.GetKeyDown (KeyCode.L)) {
var datos = new DirectoryInfo (Application.persistentDataPath + "/MarkerData/");
if (datas != null) {
FileInfo[] dataCount = datos.GetFiles ();
archivoP = new string[dataCount.Length];
position = new dataP[dataCount.Length];
//Cargar posicion de los Marcadores
for (int j = 0; j < dataCount.Length; j++) {
archivoP [j] = dataCount [j].ToString ();
position [j] = CargaMarca (archivoP [j]);
print (position [j]); ////Aqui me devuelve null
/*if (position [j] == null) {
print ("No se encontró el archivo");
} else {
marcador = Instantiate (marcadorPrefab, position [j].posicion (), Quaternion.identity) as GameObject; //print ("cargado desde archivo " + position.posicion ().ToString ());
print (position [j].posicion ().ToString ());
print (archivoP [j]);
}*/
}
}
}
//Cargar posicion de los Marcadores
dataP CargaMarca(string nombreArchivo) {
if (File.Exists (Application.persistentDataPath + "/MarkerData/" + nombreArchivo)) {
BinaryFormatter bufferData = new BinaryFormatter ();
FileStream archivo = File.Open (Application.persistentDataPath + "/MarkerData/" + nombreArchivo, FileMode.Open);
dataP salida = (dataP)bufferData.Deserialize (archivo);
archivo.Close ();
return salida;
} else {
return null;
}
}
(Ya se que es una manera chapucera de hacer las cosas, pero de momento es lo que hay. Una vez consiga que funcione ya iré cambiando cosas para hacerlo mas eficiente, pero de momento me vale con que funcione.)
Editado: Vale, acabo de arreglarlo. El problema era que en el string guardaba todo el path, y luego el el método Cargar le pasaba todo ese path a la linea:
Application.persistentDataPath + "/MarkerData/" + nombreArchivo
lo cual es igual a C:/User/.../nombrearchivo, asi que estaba haciendo como en Origen xD: C:/User/.../C:/User/.../nombrearchivo, normal que fallara
Ya funciona perfectamente.
Hola de nuevo, resulta que ha aparecido un error inesperado e inexplicable. Como ya dije, solucione el problema de cargar los marcadores, pero para mi sorpresa, cuando creo un ejecutable y hago exactamente la misma prueba, en Unity me carga perfectamente pero en el .exe solo me carga la rotación del mapa, no los marcadores. Alguien puede explicarme que clase de brujería es esta?
Saludos
Cita de: kingtrase date=1508262898No se lo que sabes sobre el Registro de windows... pero en el se guarda la configuración de tu ordenador, la de tu perfil de usuario, la información sobre programas, etc.
si crees que esos datos de carácter importante para el sistema están almacenados en un váter para que los gérmenes de vuestro juego se guarden en el pues pondré una vela cada noche por tu ordenador, pero te aseguro que yo prefiero mi váter limpio.
No has visto un registro de Windows limpio ni siquiera nada mas instalarlo .
Cita de: FNP date=1508353874No has visto un registro de Windows limpio ni siquiera nada mas instalarlo
...quizas justo despues de un "format c:/"...
Cita de: lightbug date=1507863613Yo por eso no lo incluí en la lista de posibilidades, pienso que debería ser erradicado de este mundo, su nombre tan noob me da náuseas, su existencia es como un virus que avanza y no se detiene, su utilidad, cercana a nula, si seguimos así nuestros hijos...que digo?... nuestros nietos lo van a usar para guardar sus datos! maldito seas PlayerPrefs! maldito!
jeje... justo acabo de implementar un sistema de guardado usando el #~*! playerPrefs... jejeje, se que muchos os echareis al cuello... pero
Cita de: kingtrase date=1507858520Alguien me puede explicar por que usáis playerprefs como si fuese bueno?...
normalmente solia grabar los datos en un txt... con strearRead/Writer...
pero como este proyecto va a ser multiplataforma pense que, para evitarme problemas, seria mejor hacerlo con otro sistema...
empeze a mirar lo de serializar y tal... me parecion que iba a demasiado complejo... asi que decidi guardarlo con el PlayerPrefs de mier**, (primera vez que lo uso)... y ha quedao super simple... jeje... eso si... hay que desconponer los arrais o listas en elementos... y los vectores en 3 variables floats... y cosas asi... pero funcionar funciona bien... guarda varios arrays multidimensionales... incluso un array multidimensional formado por List<Vector3>... y sin problema...
ya se que es una "guarrada" hacerlo asi, pero pasaba de todo el lio que iba a suponer serializar todo... jeje... estamos un poco con el tiempo al cuello... queremos sacar el proyecto cuanto antes... Si es caso, para la actualizacion, cambiaremos el sistema... pero de momento se queda asi...
ah, si alguien sabe de un buen tutorial de multiplayer via blueTooth... le agradeceria que me indicara donde "verlo"...
gracias, y un saludo