Introducción
En este artículo vamos a ver distintas formas de dar con las referencias de los objetos que se encuentra en la escena en Unity para usarlos dentro de un Script, esto es algo importante porque si podemos acceder al objeto, podemos manipularlo a él y a cualquiera de sus componentes como necesitemos.
Si quieres conocer con mayor profundidad sobre este tema tienes este artículo sobre qué es una referencia en programación y este otro sobre qué es una instancia en programación.
¿Cuál es nuestro Objetivo?
Estamos buscando encontrar la referencia de un GameObject que se encuentra en la jerarquía dentro de un Script e imprimir el nombre de este GameObject en consola para comprobar que se tuvo éxito. Para eso utilizaremos el Script que se observa en la figura 1, simplemente se define un dato tipo GameObject llamado “objectToFind” en la línea 8 y luego dentro de la función Start se imprime el nombre de este GameObject en consola (línea 13).
La jerarquía de la escena que vamos a usar está compuesta por los GameObjects que se observan en la figura 2, el objeto “Script-GameObject” es el que tiene asignado el Script de la figura 1 y es el que se encargará de encontrar las referencias, en la figura 3 se ve el inspector de este GameObject y el Script.
El objeto “GDT (Object to Find)” será uno de los objetos que tenemos que encontrar, así que si tenemos éxito deberíamos ver ese nombre impreso en consola.
Observación
Si en este momento entramos en el modo juego, en la consola tendremos un error de tipo “NullReferenceException”, porque el objeto al que queremos leerle su nombre es un objeto no inicializado, es decir con estado nulo. En la figura 4 vemos un ejemplo del error que aparecería en este caso.
Forma 1: Declaración del campo con visibilidad pública
Los campos y variables que se declaran dentro de un Script pueden tener tres tipos de visibilidad: “pública”, “privada” y “protegida”. Consideremos solo los casos de visibilidad privada y pública, en el primer caso el campo declarado como privado no será accesible desde contextos externos al propio Script en el que está definido, mientras que si es declarado con visibilidad pública si que se puede acceder a ese dato desde otros Scripts y en Unity además estos campos aparecerán visibles en el inspector.
En la figura 1 se declaró un campo de tipo GameObject llamado “objectToFind” sin indicarle su visibilidad, al hacer esto implícitamente se lo está declarando como privado, en la figura 5 en cambio le añadimos “public” delante para indicar que tiene visibilidad pública.
Comparando las figuras 3 y 6 vemos que ahora el campo “objectToFind” aparece en el inspector, lo que significa que podemos asignar manualmente el GameObject que queramos que esté en ese campo, es decir podemos asignar manualmente la referencia del objeto. Esto puede hacerse de dos formas, una es tomando el GameObject de la jerarquía y arrastrándolo al campo, mientras que la otra forma es utilizando el ícono del círculo con el punto que se observa a la derecha del campo (en las figura 6 o 7), esto desplegará una ventana en la que podemos elegir el objeto que queremos asignar.
Al entrar en el modo juego vemos el mensaje en consola que muestra el nombre del objeto que fue asignado en el campo, esto nos confirma que tenemos la referencia del GameObject y podemos usarla dentro del Script.
Alternativa: Declarar el campo como privado pero serializado
Si se desea mantener la visibilidad privada hay una alternativa para que el campo aparezca en el inspector, es añadir “[SerializeField]” antes de la declaración, como se observa en la figura 9.
Forma 2: Encontrar la referencia de un GameObject de la escena por su nombre
Si conocemos el nombre del GameObject en la escena que queremos acceder podemos usar esa información para dar con la referencia en nuestro Script. Para eso utilizamos el método “Find” dentro de la clase GameObject y le pasamos como parámetro el nombre del objeto que se quiere encontrar, como se observa en la figura 10.
Cuando se ejecute esa instrucción, Unity recorrerá toda la jerarquía en busca de un GameObject con ese nombre, si lo encuentra lo devuelve y la referencia nos queda en la variable “objectToFind”, pero si no lo encuentra esta variable tendrá valor null y nos dará el error de “NullReferenceException” si intentamos accederla.
Para tener en cuenta, si tenemos más de un objeto que tiene exactamente el mismo nombre, Unity nos devolverá el primero de ellos que encuentre en su registro, esto puede dar lugar a ambigüedades, es decir podríamos obtener la referencia de un objeto distinto al que estamos buscando.
Para evitar incrustar datos directamente dentro de las funciones lo que se puede hacer es definir un String con el nombre del objeto que se quiere encontrar y luego ejecutar la función “Find” pasando como parámetro el String, de esa manera evitamos hacer Hard-Coding, como se observa en la figura 11.
Forma 3: Encontrar la referencia de un GameObject de la escena por su Tag
Uno de los elementos que tiene todo GameObject en Unity es un Tag o etiqueta, se encuentra en la cabecera del inspector, como vemos en la figura 12. Podemos usar esta etiqueta para encontrar la referencia del GameObject de la escena en nuestro Script.
Por defecto los GameObjects tendrán asignado el Tag “Untagged”, pero podemos asignar uno de los Tags que vienen predefinidos o crear nuevos Tags, en las figuras 12 y 13 muestro cómo crear un nuevo Tag en Unity.
Una vez que hemos creado el Tag tenemos que asignarlo al objeto, volvemos a seleccionar el GameObject en el inspector desplegamos la ventana del Tag y lo elegimos, como se observa en la figura 14.
Ahora estamos en condiciones de encontrar ese GameObject utilizando el Tag, para ello ejecutamos el método “FindGameObjectWithTag” de la clase GameObject y pasamos como parámetro el nombre del Tag que se quiere buscar. En la figura 15 vemos esta instrucción en la línea 16, noten que el nombre del Tag se ha definido en un String en la línea 11 y luego en la línea 16 pasamos como parámetro la variable.
Cuando se ejecute esta instrucción, Unity revisará todos los objetos de la jerarquía hasta que se encuentre con un GameObject que tenga ese tag asignado, en ese punto devuelve el objeto y nos queda almacenado en el campo “objectToFind”. Si no hay ningún objeto que tenga ese Tag, el campo “objectToFind” tendrá valor null.
Para tener en cuenta, si tenemos más de un GameObject que tiene el mismo Tag asignado, Unity nos devolverá el primero de ellos que encuentre en su registro, en este caso pueden surgir ambigüedades, podríamos obtener la referencia de un objeto distinto al que queremos.
Forma 4: Referirse al mismo GameObject al cual el Script está asignado
Si el GameObject que queremos utilizar dentro de nuestro Script es precisamente el mismo GameObject al cual el Script está asignado la referencia ya está definida dentro de un campo interno de la clase MonoBehaviour, el campo se llama “gameObject” (primera letra con minúscula), este nombre hace referencia al mismo objeto al cual el Script está asignado.
En este caso no sería necesario definir el campo “objectToFind”, sin embargo vamos a inicializarlo como venimos haciendo hasta ahora. En la línea 17 vemos que asignamos “gameObject” a “objectToFind”.
Al entrar en el modo juego vemos en el inspector (figura 17) que el objeto que está en el campo “Object To Find” es el mismo objeto que tiene asignado el Script.
Forma 5: Encontrar la referencia de un GameObject que está como hijo de otro GameObject
Tal vez nos interesa obtener la referencia de un GameObject que sabemos que está como hijo de otro GameObject del que si tenemos la referencia, por ejemplo en la figura 18 vemos que el objeto ha encontrar está como hijo del GameObject que tiene asignado el script, esto lo podemos lograr porque contamos con la referencia del objeto padre y en consecuencia podemos accederlo y aplicar distintas acciones sobre él.
Así como el campo “gameObject”, el campo “transform” es otra variable que viene definida en todo MonoBehaviour y hace referencia al componente Transform del GameObject al cual el Script está asignado.
Podemos usar la Transformación para acceder a los hijos que tenga ese GameObject, utilizando el método GetChild() con parámetro 0 de la clase Transform, esto dá como resultado la transformación del primer hijo que tenga el GameObject al cual este Script está asignado, pero como nos interesa obtener la referencia del GameObject no de su Transformación, utilizamos el operador punto y el campo “gameObject”.
La instrucción queda como se observa en la línea 18 de la figura 19.
Forma 6: Encontrar la referencia de un GameObject que tiene una componente específica
Si sabemos que el GameObject que nos interesa encontrar tiene asignada una componente específica, como por ejemplo un componente “Camera”, “Rigidbody”, “AudioSource” o un Script que nostros mismo hayamos creado, podemos usar eso para dar con la referencia del GameObject.
Voy a crear un Script llamado “SomeScript” y lo voy a asignar al GameObject que queremos encontrar, como se observa en las figuras 20 y 21.
Utilizando la instrucción “FindObjectOfType<T>()”, donde T es el tipo de objeto que estamos buscando (en nuestro caso es de tipo “SomeScript”), podemos encontrar la referencia de la instancia “SomeScript” que está asignada a ese GameObject, luego usando el operador punto podemos acceder al GameObject al cual ese Script está asignado.
La instrucción que hace todo esto la vemos en la línea 20 de la figura 22.
Cuando se ejecute esta instrucción, Unity revisará todos los objetos de la jerarquía y cada una de sus componentes hasta dar con el objeto de tipo “SomeScript”, cuando lo encuentra lo devuelve como resultado, pero como nos interesa el GameObject al cual está asignado ese Script, utilizamos el punto y accedemos al campo “gameObject”. Si no hay ningún objeto que tenga asignada la componente “SomeScript” tendremos un error de referencia nula.
Para tener en cuenta, si tenemos más de un GameObject que tiene asignada la componente que estamos buscando, Unity nos devolverá la primer componente que encuentre en su registro, en este caso pueden surgir ambigüedades, podríamos obtener la referencia de un objeto distinto al que queremos.