En este Tutorial de Unity aprenderemos como Compartir Información entre distintos Scripts, conoceremos el Modificador “static”, el Modelo de programación “Singleton” y veremos un ejemplo sobre su aplicación.
Tutorial de Unity Nivel: Principiante.
10.1 ¿Por qué Comunicar Scripts?
Al momento de desarrollar un videojuego, se pueden llegar a crear una gran cantidad de Scripts (y entre mayor sea la complejidad del proyecto, mayor será la cantidad de archivos de código generados). Y en algún punto, es necesario que ciertos Scripts tomen información de otros (por ejemplo, un Script que controla las pantallas de “Game Over” de un juego, es activado desde un Script distinto que se encarga de almacenar el valor de “Salud” de nuestro personaje, para que cuando este valor llegue a cero, se termine el juego).
De esta manera, cuando decimos que hay “Scripts que se van a Comunicar”, nos referimos al hecho de que por lo menos existe “un Script” que contiene información a la cual otros pueden acceder (por ejemplo, variables a ser leídas o modificadas, o métodos a ser ejecutados).
10.2 El Modificador “static”.
Antes de profundizar en como Compartir Información entre Scripts, es importante introducir el tema del Modificador “static” (ya que es una herramienta necesaria para realizar la tarea que queremos).
El Modificador “static”, no lo debemos confundir con los Modificadores de Acceso que hemos visto en esta serie de tutoriales (“public” y “private”); ya que “static” cumple con otra función.
El Modificador “static” se usa generalmente en Clases, Variables y Métodos. Cuando es aplicado a Variables (como será nuestro caso), “static” cumple con la siguiente tarea:
- Hace que la Variable radique en la Clase y ya no en cada Instancia de esta (recordemos que una "Instancia de una Clase" se crea cuando asignamos nuestra Clase a un "Objeto del Juego" de Unity y ejecutamos la Escena/Juego). Normalmente, cuando existen Instancias de una Clase, dichas Instancias hacen copias independientes de las Variables y Métodos existentes para poder trabajar con ellos libremente (por ejemplo, cada Instancia puede modificar sus Variables y en cada Instancia la Variable puede tener un valor distinto). Pero cuando a una Variable se le asigna el Modificador “static”, se “crea una sola copia de la Variable” y todas las Instancias de la Clase la comparten (de esta forma, si la Variable se modifica, se modifica en todas las Instancias existentes).
Si quieres saber más del Modificador “static”, puedes visitar https://docs.microsoft.com/es-mx/dotnet/csharp/language-reference/keywords/static .
10.3 Comunicando Scripts.
Existen distintas maneras para que un Script pueda compartir su información, nosotros veremos solo una de ellas (una que es de gran utilidad cuando desarrollamos videojuegos). La forma con la que vamos a trabajar se basa en el Modelo de programación “Singleton”.
El Modelo “Singleton”, tiene como principal alcance “mantener a la Instancia de un Clase, como única” (normalmente, pueden existir varias Instancias de una misma Clase, pero usando el Modelo “Singleton” solo se mantiene una ejecutándose).
Al usar el Modelo “Singleton” en algún Script que contenga Modificadores de Acceso Tipo “public”, nosotros estamos definiendo que solo habrá una Instancia de ese Script en nuestro juego, y que de ella es de donde obtendremos la información (en otra palabras, estamos creando una “fuente única y confiable de información” 🙂 ).
Un ejemplo de aplicación puede ser un Script llamado “ControladorPuntuacion” que al momento de ejecutarse, su Instancia “controle” la “Puntuación” de nuestro juego (para una tarea como esta, solo se requiere una sola Instancia ejecutándose), y tener una gran cantidad de enemigos (que pueden ser muchas Instancias de otra Clase) que al ser golpeados, aumenten la “Puntuación” que llevamos (cada enemigo enviará un valor a “ControladorPuntuacion”), y al momento de alcanzar un valor específico, los enemigos restantes desaparezcan (todos los enemigos estarán leyendo constantemente una variable dentro de “ControladorPuntuacion” para saber en qué momento desaparecer).
Partiendo de este ejemplo, el Modelo “Singleton” que se aplicaría a “ControladorPuntuacion” sería:
public class ControladorPuntuacion : MonoBehaviour { public static ControladorPuntuacion instancia; void Awake ( ) { if (instancia == null) { instancia = this; } else if (instancia != this) { Destroy(gameObject); } } }
Donde:
- “public class ControladorPuntuacion : MonoBehaviour” es la estructura por defecto que nos da Unity cuando creamos una Clase o Script; la Clase “ControladorPuntuacion” se define como “public” (que puede ser llamada por Instancias de otras Clases distintas), y “: MonoBehaviour” significa que nuestra Clase pertenece a otra llamada “MonoBehaviour”, y que nuestra Clase puede hacer uso de los Métodos contenidos en “MonoBehaviour” entre muchas otras cosas más (esto tiene que ver con algo denominado “Herencia” en programación, pero será un tema que tocaremos en otros tutoriales).
- “public static ControladorPuntuacion instancia;” significa que, estamos creando una variable llamada “instancia”, esta variable tiene el Modificador de Acceso “public” (que puede ser llamada por Instancias de otras Clases distintas), además se le asigna el Modificador “static” (este hace que nuestra variable solo “exista” dentro de la Clase misma, y si se modifica, se modifica en todas las Instancias que existan). Por último, a nuestra variable “instancia” se le está asignando el Tipo de Dato “ControladorPuntuacion” (hay que recordar que los "Tipos de Datos" también pueden ser "Instancias de Clases" además de "int", "float", "string", "bool", etc.), en otras palabras, nuestra variable puede representar un Objeto o Instancia de la Clase “ControladorPuntuacion” (un Objeto o Instancia de las muchas que pueden existir).
- “void Awake ( ) { }” es un Método perteneciente a “MonoBehaviour” (o sea a Unity), el cual nos permite ejecutar su contenido una sola vez cuando todos los objetos en el juego han sido inicializados pero no habilitados para comenzar a trabajar (justo antes de que el juego comience). El Método “Awake” se ejecuta antes que el Método “Start” (el cual nos permite ejecutar su contenido una sola vez cuando todos los objetos en el juego han sido inicializados y habilitados para comenzar a trabajar), y lo usamos comúnmente para inicializar referencias que usaremos con otros Scripts u “Objetos del Juego” (objetos de la Interfaz de Edición de Unity).
- Cuando usamos “if (instancia == null)”, hay que recordar que, en el momento en el que creamos a nuestra variable “instancia” no le dimos valor alguno (nuestra variable está diseñada para almacenar a un Objeto de la Clase “ControladorPuntuacion”, pero nunca se dijo que “Objeto” de los muchos que pueden existir se debe almacenar), entonces al ejecutarse el código, nuestra variable se inicializará con el valor “null” (un equivalente a “cero”, pero para variables que almacenan Objetos). Entonces, con este condicional, estamos verificando que nuestra variable no tenga un Objeto asignado.
- Si nuestra variable “instancia” no tiene un Objeto asignado, con “instancia = this;” estamos indicando que “este Objeto o Instancia que se está ejecutando” se asigne como "Valor" a la variable “instancia”.
- Con “else if (instancia != this)” estamos diciendo que, si la variable “instancia” es distinta de “null” (o sea que ya tiene un Objeto asignado), y que si ese Objeto asignado no es “este Objeto o Instancia que se está ejecutando”, entonces se proceda a usar “Destroy(gameObject);”, que significa “destruir” a esta Instancia y al “Objeto del Juego” que la contiene en Unity.
Dándose el caso en que existan varias Instancias de la Clase “ControladorPuntuacion”, la primera de ellas que ejecute el Método “Awake”, encontrará que la variable “instancia” es igual a “null” y por consiguiente esa misma Instancia será asignada a dicha variable. Las siguientes Instancias que ejecuten el Método “Awake”, se encontrarán con que la variable “instancia” ya tiene un Objeto asignado (debido a que “instancia” tiene el Modificador “static” y su valor actual es igual en todas las Instancias que existan), dando como resultado que esas otras Instancias se “destruyan” (y ya no se ejecuten en nuestro juego).
Como resultado de todo esto, tenemos a “una sola Instancia” ejecutándose en nuestro juego (con la seguridad de que siempre habrá una sola y que será la misma), además, tenemos una “variable pública” (referenciada a esta misma Instancia) la cual podemos usar en cualquier otro Script como puente para comunicarnos.
Ha sido una explicación larga, pero tratamos de hacer que la aplicación del Modelo “Singleton” sea lo más clara posible.
Ahora, para que otro Script (el Script de los personajes enemigos) pueda tener acceso a las Variables y Métodos tipo “public” en “ControladorPuntuacion”, se necesita que presente la siguiente estructura:
public class Enemigos : MonoBehaviour { ControladorPuntuacion instanciaCtrlPuntuacion; void Start ( ) { instanciaCtrlPuntuacion = ControladorPuntuacion.instancia; } }
Donde:
- “public class Enemigos : MonoBehaviour” es la estructura por defecto que nos da Unity cuando creamos una Clase o Script;
- Con “ControladorPuntuacion instanciaCtrlPuntuacion;” estamos creando a la Variable “instanciaCtrlPuntuacion”, esta Variable tiene el Modificador de Acceso “private” porque solo la usaremos dentro del Script “Enemigos” (como no asignamos un Modificador de Acceso, automáticamente se vuelve “private”), además es Tipo “ControladorPuntuacion” (o sea que almacenará una Instancia de la Clase “ControladorPuntuacion”). Como aún no le asignamos un valor a “instanciaCtrlPuntuacion”, no sabemos que Instancia u Objeto se le debe asignar específicamente.
- Dentro del Método “Start” (que se ejecuta después del Método “Awake”), escribimos “instanciaCtrlPuntuacion = ControladorPuntuacion.instancia;”, que significa que a nuestra variable “instanciaCtrlPuntuacion” le estamos asignando la variable “instancia” de la Clase “ControladorPuntuacion” (el resultado del “Singleton”).
Así, con nuestra variable “instanciaCtrlPuntuacion”, podemos tener acceso a la Instancia de la Clase “ControladorPuntuacion”.
10.4 Comunicando Scripts en Unity.
Ahora, realizaremos un ejercicio en Unity donde comunicaremos Scripts al momento de ejecutar nuestra escena (o dicho con propiedad, comunicaremos “Instancias de Clases” u “Objetos”). Para ello, es necesario crear dos nuevos Scripts llamados “FirstScript” y “SecondScript”, después, hay que añadir estos Scripts como nuevos Componentes del objeto “GameObject” en nuestra escena de trabajo y eliminar cualquier otro Componente tipo “Script” que contenga.
Lo que haremos será lo siguiente, la Instancia de la Clase “FirstScript” pedirá información a la Instancia de la Clase “SecondScript” (a la cual se le aplicará el Modelo “Singleton”), después la misma Instancia “FirstScript” le proporcionará información y le solicitará ejecutar un Método a “SecondScript”.
El código para “FirstScript” se verá así:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class FirstScript : MonoBehaviour { //Creamos una variable para almacenar al Objeto SecondScript SecondScript instanciaSecondScript; //Creamos una variable que indique cuantas monedas hay en esta Instancia (la instancia "FirstScrip") public int primerLoteMonedas; void Start () { //Almacenamos en nuestra variable a la única Instancia de SecondScript que debe existir instanciaSecondScript = SecondScript.instancia; //Ejecutamos nuestro Método MostrarMonedas(); } //Creamos un Método que muestre la cantidad de monedas que hay en cada Instancia //Y después le pida a SecondScript que las sume y nos devuelva el resultado para mostrarlo void MostrarMonedas() { //Mostramos cuantas monedas hay en FirstScript Debug.Log("Nuestro Primer Lote tiene: " + primerLoteMonedas + " monedas"); //Le pedimos información a SecondScript y mostramos cuantas monedas tiene Debug.Log("Nuestro Segundo Lote tiene: " + instanciaSecondScript.segundoLoteMonedas + " monedas"); //Creamos una variable que almacene el resultado de la suma de las dos cantidades de monedas //La suma, es una operación que le pedimos a SecondScript que la realice y que nos devuelva el resultado int totalMonedas = instanciaSecondScript.SumarMonedas(primerLoteMonedas); //Mostarmos el resultado de la suma Debug.Log("En Total tenemos: " + totalMonedas + " monedas"); } }
Nota: Para poder llamar a un Miembro de una Clase externa, usamos "puntos" (" . ") para separa e indicar que necesitamos tener acceso a los Miembros contenidos en dichas Clases Externas.
Ejemplo 01:
Para poder tener acceso desde la Clase "FirstScript"a la variable "segundoLoteMonedas" que se encuentra en la Clase "SecondScript" (recordando que usamos "instanciaSecondScript" para hacer referencia a "SecondScript"), podemos hacerlo de la siguiente forma:
instanciaSecondScript.segundoLoteMonedas;
Ejemplo 02:
Para poder tener acceso desde "FirstScript"al Método "SumarMonedas" que se encuentra en "SecondScript" (recordando que usamos "instanciaSecondScript" para hacer referencia a "SecondScript"), podemos hacerlo de la siguiente forma:
instanciaSecondScript.SumarMonedas(aquiVaUnValor);
Ejemplo 03:
Para escribir un Mensaje en la Consola de Unity desde cualquier Script que tengamos, mandamos llamar al Método "Log" que reside en la Clase "Debug":
Debug.Log("Nuestro Mensaje");
El código para “SecondScript” se verá así:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class SecondScript : MonoBehaviour { // Creamos una variable para almacenar al Objeto SecondScript y usarla con el Modelo "Singleton" public static SecondScript instancia; //Creamos una variable que indique cuantas monedas hay en esta Instancia public int segundoLoteMonedas; // Terminamos de aplicar el Modelo "Singleton" void Awake () { if(instancia == null){ instancia = this; }else if(instancia != this){ Destroy(gameObject); } } void Start () { //Dejamos nuestro Método "Start" vacío. } //Creamos un Método que realice la suma de la cantidad de monedas que tenemos en esta Instancia // más otra cantidad a definir por quien mande llamar al Método. Después se devuelve el resultado. public int SumarMonedas (int primerasMonedas){ return primerasMonedas + segundoLoteMonedas; } }
Guardamos cada uno de nuestros dos Scripts y cambiamos a Unity.
En la ventana “Inspector” (asegúrate de tener seleccionado “GameObject” dentro de la ventana “Hierarchy”), veremos a los componentes “First Script” y “Second Script”, ahora hay que asignarles valores a cada una de sus variables como se muestra a continuación:
Y al ejecutar nuestra escena, veremos lo siguiente:
Podemos ver que nuestros dos Scripts se ejecutan correctamente y que podemos compartir información entre ellos.
Ejercicios.
Para reforzar lo aprendido, es necesario practicarlo, por ello intenta realizar los siguientes ejercicios:
- Usa los dos Scripts que creaste en este tutorial (“FirstScript” y “SecondScript”).
- En la Interfaz de Edición de Unity, elimina a “Second Script” como componente del “Objeto del Juego” “GameObject” existente. Después crea un nuevo “Objeto del Juego” (desde la barra de menú GameObject → Create Empty), posteriormente asígnale el componente “Second Script” (así tendrás a los componentes “First Script” y “Second Script” en distintos “Objeto del Juego” de Unity).
- Crea “tres” “Objeto del Juego” más y a cada uno asígnale el componente “Second Script” (en total tendrás cuatro “Objeto del Juego” que contienen solo el componente “Second Script” y solo un “Objeto del Juego” con el componente “First Script”). Después, a cada “Objeto del Juego” asígnale un valor en su campo “Segundo Lote Monedas”. Ejecuta tu escena y mira lo que sucede (presta atención en la ventana “Hierarchy” donde se encuentran los “Objeto del Juego” que creaste).
- Ahora, de los “tres” últimos “Objeto del Juego” creados, elimínales el componente “Second Script” y asígnales el componente “First Script” (en total tendrás cuatro “Objeto del Juego” que contienen solo el componente “First Script” y solo un “Objeto del Juego” con el componente “Second Script”). Después, a cada “Objeto del Juego” asígnale un valor a su campo “Primer Lote Monedas”. Ejecuta tu escena y mira lo que sucede.
Este Tutorial de Unity sobre como “Comunicar Scripts”, el Modificador “static”, el Modelo de programación “Singleton” y su aplicación termina aquí.
Este es el último tutorial de la serie “C Sharp” (C#) en Unity (nivel “Principiante”). Esperamos que los hayas disfrutado, y más importante aún, que hayas obtenido información que te sea de utilidad. Acompáñanos en las siguientes series de tutoriales, donde aprenderemos más sobre como “Realizar tus Propios Videojuegos”.
Recuerda que, si quieres conocer más a fondo sobre el lenguaje de programación “C#” puedes visitar https://docs.microsoft.com/es-mx/dotnet/csharp/ ó bien, no dudes en contactarnos para cualquier duda o asesoría haciendo clic "Aquí"
119nvu