5. CLASE OBJECT (y III)






Continuamos con las funciones de clase de la clase Object. Recordemos que las funciones de clase, como su nombre indica, no van vinculadas a objetos o instancias de una clase, sino a la clase en sí, esto es, no se utiliza el operador punto entre el nombre del objeto/instancia y el de la función, como sucedería en una función normal (o método) dentro de una clase.


FindObjectOfType

static function FindObjectOfType (type : Type) : Object


Esta función devuelve el primer objeto activo que Unity encuentre que sea del tipo que le pasamos como parámetro.

Veámoslo en un ejemplo. Recuperamos una vez más nuestro script, borramos todo y tipeamos lo siguiente:

var dameAlgo : Object;

dameAlgo = FindObjectOfType(Camera);
Debug.Log("Deberia haber encontrado 1 " + dameAlgo);

Salvamos, le damos al play y observamos que Unity ha encontrado nuestra cámara principal.

Nos advierte el manual de referencia que esta función puede ser un poco lenta y costosa en términos de rendimiento, por lo que se recomienda no usarla a su vez como parte de una función que se actualice cada frame (como por ejemplo la función Update, que estudiaremos a no mucho tardar)


FindObjectsOfType

static function FindObjectsOfType (type : Type) : Object[]


No hace falta ser muy perspicaz para darse cuenta de que esta función es idéntica a la anterior, con la diferencia de que aquí lo que se devuelve no es el primer objeto activo de tipo Type, sino que se devuelven en un array todos los objetos activos cuyo tipo coincide con el que solicitamos.

Veamos la diferencia rehaciendo nuestro script:

var dameAlgo : Object[];
var chivato : String;

dameAlgo = FindObjectsOfType(GameObject);
chivato = "He encontrado " + dameAlgo.Length + " objetos: ";

for(var contador = 0; contador < dameAlgo.Length; contador++)
chivato += dameAlgo[contador].name + "  ";

Debug.Log(chivato);
Lo que hemos hecho aquí es lo siguiente: primero hemos reconvertido nuestra variable dameAlgo en un array de Objects. Declaramos después una variable de tipo string para que vaya recopilando toda la información que al final imprimiremos. Inicializamos luego nuestro array con todos los objetos de tipo GameObject que Unity encuentre en nuestra escena. A partir de ahí,montamos un bucle for para ir añadiendo a nuestro string "chivato" el nombre de todos los objetos encontrados. Finalmente, imprimimos. Si todo ha salido bien, el resultado tendría que ser este al darle al play: DontDestroyOnLoad static function DontDestroyOnLoad (target : Object) : void
Con esta función conseguimos que el objeto que colocamos como parámetro no se destruya cuando se cargue una nueva escena. Por defecto, cuando el jugador cambia de escena o nivel, todos los objetos de la escena que abandona se destruyen antes de crear los que corresponden al nuevo nivel. Con esta función, conseguimos que los objetos que queramos pervivan al cambio de escena.

Dado que aún no sabemos cargar escenas nuevas, no vamos a realizar ningún ejemplo. Ya lo haremos cuando expliquemos las oporunas funciones.


Y con esto acabamos el análisis de nuestra primera clase. Espero que os hayan quedado los conceptos más o menos claros (al principio cuesta un poquillo, así que no desesperéis). Y por supuesto, si hay alguna duda, queja (educada) o corrección que hacer, sentiros libres de comentar.

Hasta pronto.

POSTED BY UnityScripts
POSTED IN
DISCUSSION 6 Comments

4. CLASE OBJECT (II)









FUNCIONES:

ToString:

function ToString () : String

Devuelve un string con el nombre del gameobject que hace la consulta.

GetInstanceID:

function GetInstanceID () : int

Unity asigna a cada objeto un identificador único. Esta función devuelve ese identificador.

Vamos a utilizar estas dos funciones en un mismo ejemplo. En el capítulo anterior teníamos vinculado nuestro script-para-todo a la cámara principal. Abrimos el editor de scripts, borramos todo y tecleamos lo siguiente:

var todoSobreMi: GameObject;
Debug.Log("El nombre de este objeto es " + todoSobreMi.ToString() + 
" y su id unica es " + todoSobreMi.GetInstanceID());

El script no merece mucha explicación. Asegurémonos de arrastrar el cubo a la variable todoSobreMi en el inspector. Al pulsar el play deberia mostrarse el nombre e id del cubo, tal como se muestra en la imagen (obviamente, la id no tiene por qué coincidir con la que os aparezca a vosotros)




FUNCIONES DE CLASE


operator bool, == y !=

Estas tres funciones méramente habilitan la posibilidad de establecer comparaciones de igualdad/desigualdad entre dos objetos o componentes, o (en el caso del operador bool)si existe dicho objeto componente y tiene un valor distinto de null.

Por ejemplo:

if (rigidbody) 
Debug.Log("Este gameobject tiene vinculado un Rigidbody");

Que sería lo mismo que haber escrito

if (rigidbody != null)
Debug.Log("Este gameobject tiene vinculado un Rigidbody");


Instantiate:

static function Instantiate (original : Object, position : Vector3, rotation : Quaternion) : Object

Esta función lo que hace es clonar el objeto que le pasamos como primer parámetro, y devolver ese clon del objeto original, ubicándolo en posición y rotación determinadas.

Observamos que el parámetro "position" es de tipo Vector3, que es una clase que no tardaremos mucho en estudiar. De hecho, posiblemente sea la próxima que tratemos, ya que aunque no pertenece a la jerarquía de herencia que hemos tomado como referencia para el orden de estudio, sí que va a aparecer el número suficiente de veces durante el tránsito por dicha jerarquía como para no ocuparnos de ella de primeras.

Un Vector3 es, en pocas palabras, un punto en el espacio tridimensional. Dicho punto viene dado por las coordenadas en el eje X (derecha-izquierda) Y (arriba-abajo) y Z (delante-detrás). Cada unidad se corresponde a una unidad en Unity (valga la redundancia), que a su vez equivale a un metro. Por lo tanto para que un objeto se desplace un metro a la derecha, escribiríamos en nuestro Vector3 (1,0,0).

Notemos que esto es ni más ni menos que lo que hace de manera más visual las tres variables position del transform que aparecen en el inspector.

El otro parámetro de la función, rotation, es de tipo Quaternion. Quien sepa lo que es, felicidades, yo estuve varios días peleándome con apuntes de matemáticas por la red y a lo sumo me hice una idea abstracta de que un cuaternión se compone de tres números reales y uno imaginario, el cálculo de los cuales establece la rotación de un objeto. Si os sirve de consuelo, y eso lo indica el propio manual de referencia oficial de Unity, es muy raro que trabajemos directamente con cuaterniones, sino que lo haremos con funciones que nos simplifiquen el trabajo. Yo personalmente me quedo solamente con la idea de que un cuaternión lo componen cuatro elementos y mide las rotaciones. Con eso es suficiente, creedme.

Bueno, pues dicho esto a ver si somos capaces de instanciar/clonar un cubo.

Nos vamos a Unity, y le quitamos a la cámara principal el script que le habíamos colocado anteriormente. Para hacer eso, con la cámara seleccionada en el inspector colocamos el ratón sobre el nombre del script, botón derecho=>Remove Component.

Ahora, para no andar vinculando scripts a gameobjects que nada tienen que ver con él (como hicimos no hace mucho con la cámara), vamos a crear un gameobject vacío cuya única utilidad sea contener nuestro script. Para ello nos vamos al menú superior, y le damos a GameObject=>Create empty. A la carpeta que nos aparecerá en la jerarquía la renombramos (F2) como PortaScripts.

Vale, ahora doble clic sobre nuestro script en la vista de Proyecto para abrir el editor de scipts, y escribimos:

var reproducete : GameObject;

Instantiate(reproducete, Vector3(2.0,0,0), Quaternion.identity);

Aquí lo que hemos hecho meramente es dejar una variable global "expuesta" (que pueda ser accesible desde el inspector) y llamar a la función instantiate, de tal manera que clonará el Gameobject que le arrastremos y lo situará dos unidades/metros a la derecha del objeto original. Quaternion.identity meramente significa que el objeto clonado tendrá rotación cero, esto es, su transform rotation estará a 0,0,0 (salvo que el objeto clonado dependa a su vez de otro objeto, en cuyo caso tendrá la misma rotación que el objeto padre, pero esto ya lo explicaremos cuando toque.

Salvamos el script y lo arrastramos hasta nuestro PortaScripts en la jerarquía. Vemos que en el inspector, con PortaScripts seleccionado, aparece el script y la variable expuesta que llamamos reproducete. Arrastramos el cubo hasta ella y ya podemos darle al play. Debería aparecernos un segundo cubo, tal que así:



Podemos observar alguna cosa más. En la jerarquía aparece -mientras se está reproduciendo la escena- un segundo cubo, el cual Unity nos indica expresamente que es un clon del original. Si lo seleccionamos, podemos ver en su transform en el inspector que la variable x (el eje derecha/izquierda) no está en el cero, sino en el dos, tal como le habíamos indicado.

Probemos otra cosa. Detenemos la reproducción de la escena. Seleccionamos el cubo original, y le damos a los ejes X e Y de transform rotation los valores 25 y 65 respectivamente.El cubo girará sobre dichos ejes. Démosle al play.

Podemos observar que el cubo clonado se muestra alineado con la cuadrícula global, y no con el cubo original. Esto es lo que conseguimos con Quaternion.identity.

La función instantiate es muy usada para crear proyectiles, partículas en explosiones e incluso AI (inteligencia artificial) para enemigos.

Cabe una segunda forma distinta para la función Instantiate, que es esta:

static function Instantiate (original : Object) : Object

Como podemos observar, aquí meramente estamos duplicando el objeto, se tal suerte que el clonado se ubicará en el mismo lugar y con la misma rotación que el original. Podemos probarlo en nuestro script meramente eliminando el segundo y tercer parámetro. Al darle al play, sabemos que ha aparecido un clon porque así lo indica la jerarquía, pero no podemos distinguirlo porque está justo en el mismo sitio que el original y se están solapando.


Destroy:

static function Destroy (obj : Object, t : float = 0.0F) : void

Como su nombre indica, esta función borra del juego un gameobject, un componente o un asset. Tiene dos parámetros, siendo el primero el elemento a borrar y el segundo el tiempo en segundos que tardará en borrarlo desde que se llame a la función (si no se indica lo contrario, por defecto el parámetro indica cero segundos, esto es, la destrucción del objeto es automática).

Hay una cuestión que igual ahora no se entiende muy bien, pero la dejo apuntada para tratarla más en profundidad en otro momento: si en el parámetro obt colocamos un Componente, la función sólo eliminará ese componente, haciéndolo desaparecer del Gameobject al que pertenezca. Pero si en cambio lo que le pasamos a la función es un gameobject, se destruirá tanto el gameobject como todos los hijos de ese gameobject (esto es, todos los objetos y componentes e inclusive otros gameobjects que dependan del eliminado en una relación de parentesco). Para tener un acercamiento intuitivo a esta relación de dependencia, pensemos en un gameobject coche que tiene, entre otros, cuatro componentes rueda y un gameobject conductor que hemos vinculado al gameobject coche para que allá donde se desplace el coche vaya el conductor. Si un misil destruye un componente rueda usando la función destroy, sólo se destruirá la rueda. Si en cambio lo que destruye es el gameobject coche, se destruirá tanto el vehículo como las ruedas como el conductor.

Vamos a probar la función Destroy. Para empezar devolvamos a nuestro cubo original a su rotación original (0,0,0). Vamos a rehacer ahora nuestro sufrido script, que tenemos vinculado al cubo. En el editor de scripts modificamos nuestro código así:

var reproducete : GameObject;
var nasioPaMorir : GameObject;

nasioPaMorir = Instantiate(reproducete, Vector3(2.0,0,0), Quaternion.identity);
Destroy (nasioPaMorir, 10.0); 

Creo que es bastante fácil de entender. Lo que hemos hecho es añadir una segunda variable de tipo GameObject, que no vamos a inicializar desde el inspector, porque lo que hará será almacenar el cubo clonado que devuelve la función Instantiate. Inmediatamente es llamada la función Destroy, que borrará el elemento clonado que hemos almacenado en nasioPaMorir pasados diez segundos.

Salvamos, le damos al play, contamos diez, y adiós clon.


DestroyImmediate:

static function DestroyImmediate (obj : Object, allowDestroyingAssets : boolean = false) : void

Esta función destruye inmediatamente un objeto, igual que la función anterior. Desde el manual de Unity se nos dice, no obstante, que es preferible usar Destroy en lugar de esta función, ya que puede borrar más de lo que deseamos.

Por lo tanto, olvidémonos de DestroyImmediate.

Mañana acabamos con las tres funciones de clase que nos faltan.

Bye.

POSTED BY UnityScripts
POSTED IN
DISCUSSION 2 Comments

3. CLASE OBJECT (I)



Ha llegado el momento de empezar a pelearnos con las clases que componen la API de Unity, y tal como os comentaba considero lo más conveniente empezar con la clase base.

Es sabido que en POO las clases base de las que heredan las demás suelen tener una funcionalidad bastante limitada, cuando no inexistente, siendo su función más la de punto de partida de las que habrán de heredarla que la de instanciar objetos de dicha clase. La clase Object, que es con la que vamos a empezar, no parece ser una excepción a esta regla.



La clase Object consta de dos variables, dos funciones y nueve funciones de clase (FC). Como sabréis, las funciones de clase se diferencian de las funciones standard en que aquéllas no precisan ser llamadas por una instancia u objeto de la clase, perteneciendo -como su nombre indica- a la clase y no a las instancias de dicha clase.


VARIABLES:

name:

var name : String

Bueno, esta variable ya la conocíamos. Hace referencia al nombre del objeto, y comparten ese nombre tanto el objeto en sí como los componentes de dicho objeto. Esta variable nos sirve tanto para cambiar el nombre a nuestro objeto...

name = "Cualquier nombre";

...como para obtener el nombre de un objeto.

Probemos esta segunda opción con un ejemplo muy sencillo. Abrimos Unity y nos vamos a la escena que creamos en la lección anterior. En la vista del proyecto, hagamos doble click en el script que llamamos miPrimerScript. Una vez se abre el editor, borramos todo el contenido y:


function Update(){
Debug.Log(name);
}

Como vimos en la lección anterior, Debug.Log nos permite imprimir en pantalla el valor de lo que se halle entre paréntesis. Salvamos el script (pero no cerramos el editor, meramente lo minimizamos)

Hay una cuestión que no quiero dejar pasar por alto. Lo que hemos modificado y guardado es el script que se halla en la carpeta de proyecto. No obstante, al hacer eso automáticamente se modifican todas las copias de este script que tengamos por ahí. En el caso que nos ocupa, recordaremos que a nuestro cubo le habíamos añadido una copia de este script, que a estas alturas por lo expuesto ya habrá sido modificado.

Probémoslo. Si le damos al play, observaremos que bajo la ventana game aparece el nombre de nuestro cubo.

Todo bien hasta aquí. no obstante, en la definición del manual de referencia dice que todos los componentes del objeto comparten el mismo nombre. Si echamos un vistazo a nuestro cubo en el inspector, observaremos que entre otros componentes tiene uno llamado transform, así que vamos a hacer la prueba.

Sustituimos el "name" que habíamos escrito en el editor de scripts entre paréntesis por transform.name. O sea:

Debug.Log(transform.name);

Guardamos el script, le damos al play y....Voilá!, el componente transform comparte el nombre "cubo".



hideFlags:

var hideFlags : HideFlags

Esta variable no parece a priori tener una gran utilidad. Se conseguiría con su manipulación hacer que un determinado objeto desaparezca o bien del inspector, o bien de la jerarquía, o impedir que dicho objeto se pueda editar, o no permitir que un objeto sea salvado con la escena y en consecuencia destruído con la nueva escena que se cargue.

Para tal fin esta variable manera las diferentes opciones de una de las muchas enumeraciones que hay en Unity. Esta enumeración, de nombre HideFlags, tiene las siguientes opciones:

HideInHierarchy____El objeto no aparecerá en la jerarquía.
HideInInspector____El objeto no aparecerá en el inspector.
DontSave_____El objeto no será salvado en la escena ni destruido en la nueva que se cargue.
NotEditable______El objeto no será editable en el inspector.
HideAndDontSave___Combinación de HideInHierarchy y DontSave.


Así, si por ejemplo quisiéramos (no se me ocurre muy bien para qué) que el cubo desapareciera del inspector mientras se desarrolla el juego, le vincularíamos un script parecido a este:

var meEvaporo: GameObject;meEvaporo.hideFlags = HideFlags.HideInInspector;

Este script lo podríamos arrastrar a la cámara principal, y con posterioridad arrastrar nuestro cubo en la variable global meEvaporo, que está esperando un gameObject. Observaremos que cuando le demos al play, el cubo desaparece del inspector.

¿Por qué meEvaporo es de tipo GameObject?¿No debería tratarse de un Object, que es la clase que estamos tratando?. Pues efectivamente, pero sucede que si sustituimos GameObject por Object en nuestro script, comprobaremos que desaparece del inspector la variable meEvaporo, posiblemente por lo que decía al principio de la lección de que la clase Object está pensasa como clase base para dotar de funcionalidad a las que la heredan, que para ser instanciada ella misma.

En cualquier caso, como digo, no parece que hideFlags sea una variable que vayamos a usar mucho.

Y hasta aquí por hoy. En el próximo capítulo seguiremos con las funciones de esta clase.

POSTED BY UnityScripts
POSTED IN
DISCUSSION 2 Comments
Con la tecnología de Blogger.