viernes, 3 de junio de 2016

Hostear juego webgl en Google Drive

Actualización:
Google Drive ha detenido este servicio por lo tanto  ya no se pueden hostear juegos en Google Drive como lo indica este tutorial.



Desde mi último demo sobre serialización muchos me han preguntado como hice para hostear mi juego en google drive así que hoy decidí explicar como se hace, de hecho es muy sencillo, empecemos :

¿Qué necesitamos?
-Build del juego en webgl
-Cuenta Google Drive

Al compilar un juego para webgl, el build se verá algo así como la siguiente imagen:




Nos vamos a Google Drive y creamos una carpeta donde guardaremos nuestro build, dentro de la carpeta agregaremos los archivos que nos generó el build, quedaría algo como esto...



Luego nos vamos a la carpeta superior que acabamos de crear  y le damos clic derecho en compartir



Le damos a la opción "Avanzada"



Luego en cambiar...



Y por último en público en la web



Le damos guardar y nos devolverá a la pantalla anterior, aquí podremos ver el link para compartir, tendremos que hacerle algunos ajustes al link, podemos copiarlo y pegarlo en un blog de notas para hacer los ajustes necesarios



Ahora viene el truco, este link que nos otorga google drive sirve para compartir el archivo pero no para hostear el juego así que debemos modificarlo

Por ejemplo el link de mi carpeta : https://drive.google.com/folderview?id=0B3m1t7GMZrQCYTBFYmNZX2hXRlk&usp=sharing

Lo que nos interesa es el id, que viene después de 'id=' y antes de '&' en el caso de mi link mi id sería = 0B3m1t7GMZrQCYTBFYmNZX2hXRlk

Ahora antes del id pondremos :

https://googledrive.com/host/

Así que el nuevo link quedaría de esta manera :

https://googledrive.com/host/0B3m1t7GMZrQCYTBFYmNZX2hXRlk

Si todo salió bien al poner el link en el navegador este se transformará en uno más complejo y empezará a cargar nuestro juego



Y eso es todo! espero les funcione y puedan sacarle provecho para presentar demos de sus juegos o compartir su juego si no tienen un servidor donde hostearlo, como siempre dudas y comentarios dejarlas abajo o escribirme a darkyashamaru@gmail.com.

domingo, 29 de mayo de 2016

Guardar la posición y rotación de un personaje

Esta vez quería traer un ejemplo un poco más práctico para que se entienda mejor el tema de la serialización.


Aquí la demostración con un demo: Link al demo
Aquí el link al proyecto para que analicen como está hecho : Link al proyecto


Debido a que el demo es web y no me deja guardar archivos en el computador del usuario, usé 2 métodos de serialización que vimos en el articulo pasado, PlayerPrefs y JSON.

Si por alguna razón no puedes abrir el proyecto, o por algún otro inconveniente dejaré el script más importante escrito aquí y lo dejaré comentado paso a paso:

using UnityEngine;
using UnityEngine.SceneManagement;

public class PlayerPosition : MonoBehaviour {
    //llave usada para guardar los datos en PlayerPrefs
    const string PositionKey = "PlayerPosition";

    void OnGUI ()
    {
        //Titulo del demo
        GUILayout.Label("Serialization Example");

        //Espacio en la GUI
        GUILayout.Space(20);

        //Instrucciones para mover el control
        GUILayout.Label("Use WASD or arrow keys to move");

        //Botón que llama al método de guardar posición
        if (GUILayout.Button("Save Position",GUILayout.Width(120)))
        {
            SavePosition();
        }

        //Botón que llama al método cargar posición
        if (GUILayout.Button("Load Position", GUILayout.Width(120)))
        {
            LoadPosition();
        }

        //Botón que llama al método recargar escena
        if (GUILayout.Button("Reload Scene", GUILayout.Width(120)))
        {
            Reset();
        }
    }


    public void SavePosition()
    {
        //Creamos un objeto de tipo PositionData (Clase personalizada que está más abajo) y la inicializamos con la posición y rotación de nuestro personaje
        PositionData pd = new PositionData(transform.position, transform.rotation);

        //Serializamos y guardamos en PlayerPrefs el string que nos devuelve la serialización a JSON
        PlayerPrefs.SetString(PositionKey, JsonUtility.ToJson(pd));
    }

    public void LoadPosition ()
    {
        //Deserializamos el string obtenido de PlayerPrefs y lo guardamos en un objeto de tipo PositionData (Clase personalizada que está más abajo)
        PositionData pd = JsonUtility.FromJson<PositionData>(PlayerPrefs.GetString(PositionKey));
//Verificamos que el objeto no sea nulo (en el caso que se trate de cargar antes de haber guardado al menos 1 vez
        if(pd!=null)
        {
            //Asignamos la posición guardada a nuestra posición actual
            transform.position = pd.Position;

            //Asignamos la rotación guardada a nuestra rotación actual
            transform.rotation = pd.Rotation;
        }
    }

    public void Reset ()
    {
        //Recargamos la escena actual
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }
}

//Simple clase personalizada usada para serializar la posicion y rotacion de nuestro personaje
[System.Serializable]
public class PositionData
{
    //Posición a serializar
    public Vector3 Position;

    //Rotación a serializar
    public Quaternion Rotation;

    //Dejamos un constructor vacío porque algunos tipos de serializacion así lo requieren
    public PositionData (){}

    //Creamos un constructor custom para inicializar fácilmente nuestra clase
    public PositionData (Vector3 pos, Quaternion rot)
    {
        Position = pos;
        Rotation = rot;
    }
}

Básicamente lo que hace este script es que cuando se presiona el botón de guardar la posición, serializamos la posición y la rotación de nuestro personaje y como la serialización a JSON nos devuelve un string este lo guardamos usando PlayerPrefs y para cargar la posición hacemos el proceso inverso, de esta manera aunque recarguemos la escena o cerremos el juego y lo volvamos a abrir en otra ocasión, la posición que guardamos la podemos volver a cargar.

sábado, 28 de mayo de 2016

Serialización

La serialización, según wikipedia, "consiste en un proceso de codificación de un objeto en un medio de almacenamiento (como puede ser un archivo, o un buffer de memoria) con el fin de transmitirlo a través de una conexión en red como una serie de bytes o en un formato humanamente más legible como XML o JSON, entre otros."

Esto en palabras más sencillas es la manera de guardar los datos de nuestro juego en formatos universales que pueden ser guardados en el disco duro, ser usados por otros programas etc.


Empezaremos por analizar las distintas formas que Unity tiene para serializar y luego haremos unos ejemplos prácticos guardando el estado de objetos en la escena de Unity.

Existen muchas formas de serializar datos en Unity, explicaré las más comunes y las que he usado con más frecuencia, entre las opciones tenemos, playerpref, scriptableObjects, xml, json, binario, todos estos métodos no son intercambiables, más bien cada uno cumple su propia función y muchas veces se usan varios métodos al tiempo, explicaré uno por uno.

Formas de serializar exclusivas de Unity


PlayerPrefs

Abreviatura de "Player Preferences" o preferencias de usuario, como su nombre lo indica está diseñado para guardar datos como el nivel de volumen, el lenguaje, y otras cosas que queremos que se mantengan entre secciones pero ningún dato importante para un juego como por ejemplo el dinero, debido a que los usuarios podrán modificar fácilmente estos datos.

Su uso es muy sencillo, pueden guardar int, floats y strings, en vez de reinventar la rueda aquí está el link a la documentación de Unity donde está muy claro.


ScriptableObject


Los scriptableObjects son assets que podemos guardar en nuestro proyecto, son mayormente usados para guardar datos, por ejemplo, una base de datos de items, armas, personajes, etc.

Para crear un scriptableObject se requieren 2 pasos, una clase que herede de ScriptableObject y agregarlo al menu para crearlo fácilmente o también hacer un pequeño script de editor que creará nuestro asset en el proyecto,
Paso 1, Clase que herede de scriptableObject :

using UnityEngine;
using System.Collections;

public class TestScriptableObject : ScriptableObject {


}

Podemos borrar los metodos Start y Update, los ScriptableObjects no hacen uso de ellos

Paso 2, Hacer que aparezca en el menú para crearlo fácilmente sin uso de script de editor

using UnityEngine;
using System.Collections;

[CreateAssetMenu(fileName = "Test", menuName = "Custom/ScriptableObject")]
public class TestScriptableObject : ScriptableObject {


}

Con el atributo [CreateAssetMenu()] podemos hacer que en el menú nos aparezca la opción de crear nuestro scriptableObject, "filename" es el nombre por defecto que le queremos dar a nuestro scriptableObject y "menuName" son los submenú en donde aparecerá, será más fácil de entender con una imagen :


Al seleccionar la opción nos creará nuestro scriptableObject y le pondrá el nombre por defecto que agregamos en código pero lo podremos cambiar :




La otra opción para crearlo es creando un sencillo script de editor, recuerden que los scripts de editor deben estar dentro de una carpeta llamada "Editor" si no hacemos esto, recibiremos un error cuando queramos compilar nuestro proyecto, aquí el script :

using UnityEngine;
using System.Collections;
using UnityEditor;

public class CreateScriptableObject
{
    //Con este atributo hacemos posible el llamar a este metodo desde el menú
    [MenuItem("Assets/Create/Custom/ScriptableObject")]
    public static void CreateMyAsset()
    {
        //Creamos el scriptableObject llamando al metodo estatico ScriptableObject.CreateInstance y guardandolo en una variable
        TestScriptableObject so = ScriptableObject.CreateInstance<TestScriptableObject>();

        //Con este metodo creamos el asset en el proyecto
        AssetDatabase.CreateAsset(so, "Assets/Test.asset");

        //Importante guardar los assets para que se escriban los cambios al disco duro
        AssetDatabase.SaveAssets();

        //Enfocamos la ventana del proyecto
        EditorUtility.FocusProjectWindow();

        //Seleccionamos el objeto que acabamos de crear
        Selection.activeObject = so;
    }
}

Este funcionará muy parecido al primer método.
Ahora que ya sabes como crear un scriptableObject este puede ser usado para guardar muchos datos, personalmente lo uso como una base de datos, ya que no es tan práctico para guardar datos en tiempo real, sin embargo puede ser usado para ello.
Para guardar datos sólo basta con crear variables en el script y estas se reflejarán en el inspector donde pueden ser alimentadas, también pueden ser arrastrados como variables a otros scripts y ser alimentado desde ellos.

Ejemplo base de datos :


Obviamente una base de datos como la de la imagen está fuera del enfoque de este tutorial pero quería mostrarles un ejemplo. Con scripts de editor y scriptableObjects puedes hacer maravillas.

Formas de serializar independientes de Unity

Los métodos anteriores que vimos son dependientes de Unity, ahora veremos las formas de serializar más estandarizadas a través de diferentes software, antes de analizar xml, json y binario quiero explicar las bases de la serialización, al serializar una clase guardaremos los campos públicos de esta, además en la mayoría de los casos la clase debe tener el atributo [System.Serializable] antes de poder ser serializada.

A que me refiero con los campos públicos? 




Al serializar guardaremos los valores de los campos resaltados en la imagen y si la clase hereda de otra, las dos deben tener el atributo [System.Serializable] es por esto y por cuestión de optimización que debemos evitar serializar clases que hereden de Monobehaviour o alguna otra clase de Unity, ya que tienen muchos datos que no necesitaremos, además de que algunas de las clases puedan no contener el atributo para serializar correctamente.


XML y Binario

Según wikipedia : "XML, siglas en inglés de eXtensible Markup Language ("lenguaje de marcas Extensible"), es un lenguaje de marcas desarrollado por el World Wide Web Consortium (W3C) utilizado para almacenar datos en forma legible."

Sencillamente se trata de un lenguaje o tipo de archivo en el que podemos serializar nuestros datos y a diferencia del binario estos pueden ser parcialmente o completamente leídos por humanos.

Ejemplo de como se ve un XML : 


Manos a la obra, veamos como serializar en xml, hay más de una manera de hacerlo, .Net nos trae intregadas varias maneras de hacerlo, veamos mi manera habitual:

Ejemplo sencillo de serialización en xml :

using UnityEngine;
using System.Collections;

//Este namespace como su nombre lo indica debemos agregarlo para acceder a la serializacion en xml
using System.Xml.Serialization;
//Este namespace es el que se encarga del manejo de archivos
using System.IO;

public class TestSerializationXML : MonoBehaviour {

 void Start () {
        //Creamos una variable del tipo de la clase que vamos a serializar
        ClassToSerialize test = new ClassToSerialize();

        //Le damos valores al primer campo publico
        test.PublicField1 = 150;

        //Le damos valor al segundo campo publico
        test.PublicField2 = "Hola Mundo";

        //Creamos una instancia de XmlSerializer usando el tipo de nuestra clase a serializar
        XmlSerializer serializer = new XmlSerializer(typeof(ClassToSerialize));

        //Aquí elegimos la ubicación y el nombre del archivo, en este caso uso la ruta de los assets + MyXML más el formato .xml
        FileStream stream = new FileStream(Application.dataPath + "/MyXML.xml", FileMode.Create);

        //Es importante elegir el tipo de Encoding a UTF8 para no tener problemas con ñ o tildes 
        StreamWriter streamWriter = new StreamWriter(stream, System.Text.Encoding.UTF8);

        //Aquí agregamos el objeto que creamos para serializarlo
        serializer.Serialize(streamWriter, test);

        //Cerramos el archivo para no tener errores cuando otro proceso intente acceder a el
        stream.Close();
        streamWriter.Close();
    }

}

//Clase para serializar
[System.Serializable]
public class ClassToSerialize
{
    public int PublicField1;
    public string PublicField2;

    public void Method1()
    {
        //hacer algo
    }
}

Al agregar ese script a un objeto en escena y dar clic en play obtendremos en nuestros assets un archivo llamado MyXML.xml con el siguiente contenido :
(Si no logras ver el archivo en el proyecto presiona Control + R, o minimiza y unity y vuelve a abrirlo para actualizar el proyecto)



Como se puede apreciar, los datos que llenamos se ven reflejados en el archivo, sin embargo el script que usamos parece un poco complicado, vamos a simplificar la serialización usando un método que nos facilite la vida:

public static void SaveXML<T>(string path, string fileName, object data) where T : class
    {
        //Verificamos que la ruta de guardado exista antes de intentar guardar el archivo, sino existe esta es creada
        Directory.CreateDirectory(path).Create();

        //Creamos la instancia de la clase XmlSerializer con el tipo deseado
        XmlSerializer serializer = new XmlSerializer(typeof(T));

        //Agregamos la extensión .xml al nombre del archivo si este no lo contiene
        if (!fileName.Contains(".xml"))
            fileName += ".xml";

        //Esta clase se encarga de la manipulación de archivos, creamos una instancia en modo crear, 
        //de esta manera si no existe el archivo se crea, usando la ruta y el nombre del archivo especificada
        FileStream stream = new FileStream(path + fileName, FileMode.Create);

        //Esta clase es la que se encargará de escribir el contenido en el archivo
        StreamWriter streamWriter = new StreamWriter(stream, System.Text.Encoding.UTF8);
        serializer.Serialize(streamWriter, data);

        //Cerramos el archivo para que esté disponible para otros procesos
        stream.Close();
        streamWriter.Close();
    }


De está manera serializar en XML será tan sencillo como llamar el método y dar los valores necesarios :
SaveXML<Tipo>("ruta", "nombre del archivo", datos);

Veamos como queda con el ejemplo usado antes :

public class TestSerializationXML : MonoBehaviour {

 void Start () {
        //Creamos una variable del tipo de la clase que vamos a serializar
        ClassToSerialize test = new ClassToSerialize();

        //Le damos valores al primer campo publico
        test.PublicField1 = 150;

        //Le damos valor al segundo campo publico
        test.PublicField2 = "Hola Mundo";

        SaveXML<ClassToSerialize>(Application.dataPath+"/", "MyXML.xml", test);
}

    public static void SaveXML<T>(string path, string fileName, object data) where T : class
    {
        Directory.CreateDirectory(path).Create();
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        if (!fileName.Contains(".xml"))
            fileName += ".xml";
        FileStream stream = new FileStream(path + fileName, FileMode.Create);
        StreamWriter streamWriter = new StreamWriter(stream, System.Text.Encoding.UTF8);
        serializer.Serialize(streamWriter, data);
        stream.Close();
        streamWriter.Close();
    }

}

Ahora que ya sabemos como guardar un XML, veamos como es el proceso inverso de cargar desde un archivo XML, está vez usaremos inmediatamente un método para ello :

public static T LoadXml<T>(string path, string fileName) where T : class
    {
        //Creamos la instancia de la clase XmlSerializer con el tipo deseado
        XmlSerializer serializer = new XmlSerializer(typeof(T));

        //Verificamos si el archivo existe antes de intentar leerlo
        if(File.Exists(path+fileName))
        {
            //Esta clase se encarga de la manipulación de archivos, creamos una instancia en modo abrir
            FileStream stream = new FileStream(path + fileName, FileMode.Open);

            //Creamos una instancia de StreamReader la clase que se encarga de leer el archivo
            StreamReader sr = new StreamReader(stream, System.Text.Encoding.UTF8);

            //Creamos una variable de tipo T (el tipo T representa el tipo que usamos al llamar el metodo)
            T t = serializer.Deserialize(sr) as T;

            //Cerramos el archivo para que esté disponible para otros procesos
            stream.Close();
            sr.Close();

            //Regresamos el objeto deserializado
            return t;
        }
        //Si no existe el archivo regresamos null indicando que no encontramos nada
        return null;
    }

Veamos su uso cargando el archivo que creamos anteriormente :

void Start ()
    {
        ClassToSerialize test = LoadXml<ClassToSerialize>(Application.dataPath+"/", "MyXML.xml");
        if(test!=null)
        {
            Debug.Log(test.PublicField1);
            Debug.Log(test.PublicField2);
        }
    }

El resultado en la consola :


Para serializar en binario sólo basta con cambiar el método que usamos y la extensión del nombre del archivo a .bin

Quedarían de la siguiente forma :

public static void Save(string fileName, object data)
    {
        BinaryFormatter bf = new BinaryFormatter();
        Stream stream = new FileStream(Application.persistentDataPath + fileName, FileMode.Create);
        bf.Serialize(stream, data);
        stream.Close();
    }

    public static T Load(string fileName) where T : class
    {
        BinaryFormatter bf = new BinaryFormatter();
        Stream stream = new FileStream(Application.persistentDataPath + fileName, FileMode.Open);
        T t = (T)bf.Deserialize(stream);
        stream.Close();
        return t;
    }

La serialización en binario es muy poderosa, puede guardar cualquier tipo de archivo, guardar grafos, arboles, referencias, arrays multidimensionales, el único problema es que el archivo resultante no es legible por humanos, por lo tanto es un poco más complicado a la hora de debugear o editar externamente.

JSON

Según wikipedia : "JSON, acrónimo de JavaScript Object Notation, es un formato de texto ligero para el intercambio de datos. JSON es un subconjunto de la notación literal de objetos de JavaScript aunque hoy, debido a su amplia adopción como alternativa a XML, se considera un formato de lenguaje independiente."

Básicamente es como el XML pero más ligero y sencillo, muy utilizado actualmente.

Desde la versión 5.3 Unity nos ha facilitado la vida incluyendo su sencillo serializador json, de otra manera hay que buscar un framework (hay muchas opciones y gratuitas).


Al usar el serializador de Unity este nos devolverá un string, el cual tendremos que guardar en un archivo, primero hagamos un ejemplo imprimiendo en la consola:

using UnityEngine;
public class TestSerializationXML : MonoBehaviour {

 void Start () {
        //Creamos una variable del tipo de la clase que vamos a serializar
        ClassToSerialize test = new ClassToSerialize();

        //Le damos valores al primer campo publico
        test.PublicField1 = 150;

        //Le damos valor al segundo campo publico
        test.PublicField2 = "Hola Mundo";

        Debug.Log(JsonUtility.ToJson(test));
    }
}

//Clase para serializar
[System.Serializable]
public class ClassToSerialize
{
    public int PublicField1;
    public string PublicField2;

    public void Method1()
    {
        //hacer algo
    }
}


El resultado :



Como podemos ver es muy fácil de usar, Tan sólo JsonUtility.ToJson( Objeto a serializar ) y nos devolverá un string con el objeto serializado.

Ahora veamos como guardar ese string a un archivo, usaremos el siguiente metodo :

public static void TextWriter(string path, string fileName, string text)
    {
Directory.CreateDirectory(path).Create();
        using (StreamWriter writetext = new StreamWriter(path + fileName))
        {
            writetext.WriteLine(text);
        }
    }

Veamos como queda todo el script :

using UnityEngine;
using System.Collections;
using System.IO;

public class TestSerializationXML : MonoBehaviour {

 void Start () {
        //Creamos una variable del tipo de la clase que vamos a serializar
        ClassToSerialize test = new ClassToSerialize();

        //Le damos valores al primer campo publico
        test.PublicField1 = 150;

        //Le damos valor al segundo campo publico
        test.PublicField2 = "Hola Mundo";

        string serializedText = JsonUtility.ToJson(test);

        Debug.Log(serializedText);

        TextWriter(Application.dataPath + "/", "JSONTest.json", serializedText);
    }

    public static void TextWriter(string path, string fileName, string text)
    {
        Directory.CreateDirectory(path).Create();
        using (StreamWriter writetext = new StreamWriter(path + fileName))
        {
            writetext.WriteLine(text);
        }
    }
}

//Clase para serializar
[System.Serializable]
public class ClassToSerialize
{
    public int PublicField1;
    public string PublicField2;

    public void Method1()
    {
        //hacer algo
    }
}

Al agregar el anterior script a un objeto en escena y darle clic a play se creará un nuevo archivo en los assets llamado JSONTest.json
(Si no logras ver el archivo en el proyecto presiona Control + R, o minimiza y unity y vuelve a abrirlo para actualizar el proyecto)



Ahora procedemos a cargar el archivo, usaremos el siguiente método :

public static string TextReader(string path, string fileName)
    {
        //Inicializamos una variable de tipo string como string vacia
        string result = string.Empty;

        //Verificamos que el archivo exista
        if (File.Exists(path+ fileName))
        {
            //Creamos la instancia de streamReader con la ruta y el nombre del archivo
            using (StreamReader readtext = new StreamReader(path+ fileName))
            {
                //Guardamos en la variable todo el texto del archivo
                result = readtext.ReadToEnd();
            }
        }
        else
        {
            //Si no se encontró el archivo imprimimos un mensaje
            Debug.Log("No se encontró archivo en la ruta : " + path);
        }
        //Regresamos el resultado
        return result;
    }

Ahora lo usaremos para cargar el archivo e imprimir los resultados:
using UnityEngine;
using System.Collections;
using System.IO;

public class TestSerializationXML : MonoBehaviour
{

    void Start()
    {
        string text = TextReader(Application.dataPath + "/", "JSONTest.json");
        if (!string.IsNullOrEmpty(text))
        {
            ClassToSerialize test = JsonUtility.FromJson<ClassToSerialize>(text);
            Debug.Log(test.PublicField1);
            Debug.Log(test.PublicField2);
        }
    }

    public static string TextReader(string path, string fileName)
    {
        string result = string.Empty;
        if (File.Exists(path + fileName))
        {
            using (StreamReader readtext = new StreamReader(path + fileName))
            {
                result = readtext.ReadToEnd();
            }
        }
        else
        {
            Debug.Log("No se encontró archivo en la ruta : " + path);
        }
        return result;
    }
}

//Clase para serializar
[System.Serializable]
public class ClassToSerialize
{
    public int PublicField1;
    public string PublicField2;

    public void Method1()
    {
        //hacer algo
    }
}

Resultado en la consola :


De esta manera aprendimos a serializar nuestros archivos de diferentes maneras, pero puede que no esté del todo claro como podemos usar la serialización en nuestros juegos, en el siguiente post, debido a que este se hizo más grande de lo que esperaba, haré un ejemplo práctico con cada uno de los métodos de serializacion, Si no entiendes algo, por favor envíame un correo a darkyashamaru@gmail.com o déjalo en los comentarios y trataré de explicarlo de mejor manera.

domingo, 22 de mayo de 2016

Unity Events


Antes de seguir con las clases personalizadas y usos prácticos en Unity quería mostrarle los eventos eventos de Unity, esos eventos que vemos en las interfaces UI y que podemos usar en nuestros propios scripts, aunque son muy útiles y fáciles de usar por alguna razón no están muy documentados, para aprender de ellos tuve que analizar el código de fuente del UI.

Procedamos a hacer nuestro propio evento :


using UnityEngine;
using System.Collections;
using UnityEngine.Events;

public class TestUnityEvent : MonoBehaviour {

    public UnityEvent OnStart = new UnityEvent();

 // Use this for initialization
 void Start () {
        OnStart.Invoke();
    }
 
 // Update is called once per frame
 void Update () {
 
 }
}

Primeramente agregamos using UnityEngine.Events; en la parte superior de nuestro código, luego creamos un evento de tipo UnityEvent y le ponemos el nombre que queramos, en este caso le puse OnStart porque lo ejecutaré en el metodo Start, luego para llamar al evento usamos el nombre del evento mas .Invoke() de esta manera se ejecuta el evento, como pueden ver es muy fácil, al agregar el script a un objeto en escena se verá de la siguiente manera:


De esta manera podemos usar estos útiles eventos que son muy dinámicos ya que nos dejan de manera visual ejecutar métodos de otros scripts, modificar objetos en la escena, etc.

Si necesitamos enviar un valor junto con el evento, por ejemplo como el que usa el InputField el cual nos envía un string cada vez que el usuario actualiza el valor o cuando presiona enter para terminar :


Para hacer este tipo de evento basta con que hagamos una clase que herede de UnityEvents y entre los simbolos ' < > ' agreguemos el valor que queremos que devuelva aquí el codigo ejemplo para string

[System.Serializable]
public class UnityEventString : UnityEngine.Events.UnityEvent<string>
{

}

La clase no necesita tener nada en especial, sólo que herede de UnityEngine.Events.UnityEvent y declare los tipos que quiere devolver además de usar el atributo System.Serializable como expliqué en mi anterior post el atributo sirve para mostrar las clases en el inspector de Unity (en realidad hace más que eso, pero en este caso lo usamos para eso). Para usarla debemos declarar una variable del tipo de la clase que hicimos e invocar nuevamente el metodo ".Invoke( valorString )"

using UnityEngine;
using System.Collections;
using UnityEngine.Events;

public class TestUnityEvent : MonoBehaviour {

    public UnityEvent OnStart = new UnityEvent();
    public UnityEventString OnStart2 = new UnityEventString();

    // Use this for initialization
    void Start () {
        OnStart.Invoke();
        OnStart2.Invoke("Hola mundo");
    }
 
 // Update is called once per frame
 void Update () {
 
 }
}

[System.Serializable]
public class UnityEventString : UnityEngine.Events.UnityEvent<string>
{

}



De esta simple manera podemos usar estos poderosos y dinámicos eventos en nuestros juegos, cualquier duda o sugerencia al respecto dejarla en los comentarios!.

martes, 17 de mayo de 2016

Clases personalizadas

English Version

Analizando las preguntas frecuentes en los foros y debido a que luego de aprender a usar arrays y listas en programación queremos solucionar todos nuestros problemas con ellas, se vuelve un poco difícil manejar los indices y nuestro código no queda tan limpio como quisiéramos, además las arrays de más de una dimensión son aún más complicadas, quiero enseñarles una alternativa, las clases personalizadas, con ellas podemos manejar muchos valores y podemos darle muchos usos, en este caso quiero enseñarlas con un ejemplo muy básico para que se entienda la idea principal y luego otro un poco más complejo aplicado en un inventario básico.

¿A que me refiero con una clase personalizada?


Una clase personalizada es muy común en el mundo de la programación, es cualquier clase que no trae el lenguaje por defecto, pero para personas que no estén completamente familiarizados con los lenguajes orientados a objetos o que como yo empezaron a usar Unity antes de aprender a programar por completo, resulta un poco complicado, yo por ejemplo pensaba que todas las clases debían heredar de MonoBehaviour

Una clase personalizada es tan fácil como esto:

public class ClasePersonalizada {
    public int Atributo1;
    public string Atributo2;
}

Estas clases pueden estar junto a otras clases en un mismo archivo y a diferencia de las clases que heredan de MonoBehaviour el nombre del archivo no necesita ser igual al nombre de la clase.
Sin embargo al no heredar de MonoBehaviour perdemos algunas ventajas, como la posibilidad de agregarse como componente a objetos, los metodos llamados automaticamente como Start, Update, Awake, etc, entre muchas otras, pero también se tienen otras ventajas como la posibilidad de serializarlas (Esto lo explicaré en otra entrada más adelante), también las hace mucho más optimizadas a la hora de tener una jerarquía grande de clases como es el caso de cuando se hace un inventario complejo y otras ventajas que ire cubriendo en otras entradas.

Hacer una clase y exponer sus valores en el editor de Unity

Las clases personalizas al no poder agregarse como componente a un objeto de la escena de Unity no es posible verlas en el inspector, incluso si hacemos que una clase MonoBehaviour tenga una variable del tipo de nuestra clase personalizada está no aparecerá en el inspector, veamos un ejemplo:

Tenemos el siguiente script : 

using UnityEngine;
using System.Collections;

public class CustomClassesTutorial : MonoBehaviour { 
    public Item TestItem;
}

public class Item {
    public int Id;
    public int price;
    public Sprite Image;
}


Al agregar este script a un objeto en unity veremos lo siguiente :



Como se puede apreciar en la imagen la variable de tipo Item no es visible en el inspector, esto se puede arreglar tan sólo con ponerle el atributo [System.Serializable] antes de declarar la clase o agregando arriba "using System;" y posteriormente sólo utilizando [Serializable]:

using UnityEngine;
using System.Collections;

public class CustomClassesTutorial : MonoBehaviour { 
    public Item TestItem;
}

[System.Serializable]
public class Item {
    public int Id;
    public int price;
    public Sprite Image;
}

Y el nuevo resultado se vería de esta manera:


Como pueden apreciar ahora el inspector muestra los atributos de la clase Item.

Bueno ahora que ya sabemos que es una clase personalizada, como crearlas y mostrarlas en el inspector, en mi próximo post iremos por un ejemplo en donde realmente podamos apreciar su uso.