Curso Unity Script

237
0. PRELIMINARES Y ALGUNOS PRESUPUESTO S

Transcript of Curso Unity Script

  • 0. PRELIMINARES Y ALGUNOS PRESUPUESTO S

  • Bienvenid@s a unityscript.

    Antes de entrar en materia y para que nadie se lleve a engao, considero honesto advertir a los hipotticos lectores de que el autor de estas lneas no es programador profesional, ni ha creado ninguno de los cien videojuegos ms vendidos de la historia. De hecho, si he de ser totalmente sincero, hace menos de tres aos este que os est escribiendo no haba escrito ni una sola lnea de cdigo.

    Puede parecer por lo tanto un atrevimiento -y de hecho lo es- que un tipo como yo, un autodidacta sin ninguna base acadmica, se atreva a inaugurar un blog de estas carcteristicas, y comprender perfectamente que alguien con conocimientos profundos sobre programacin en general o Unity en particular salga corriendo de este site.

    Reconozco ya de antemano, por lo expuesto, que muy probablemente algunas de las cosas que os vaya explicando en las lecciones que vendrn a continuacin puedan contener errores, y pido disculpas anticipadas por ello. Huelga decir que agradecer cualquier correccin por parte de personas con ms conocimientos.

    Me gustara explicaros brevemente las razones por las cuales me he decidido a crear este blog, las razones por las que creo que este blog es necesario:

    Yo aterric en el universo Unity hace unos cuatro meses. Ms o menos como todos, me di una vuelta por la interfaz grfica, estuve pelendome con las cmaras, encend las luces y puse niebla a la escena, traste con el terrain hasta crear algo parecido a una isla, import algunos modelos de Blender (todava ando buscando las texturas de esos modelos) y cosas parecidas.

    Luego empec a leerme todos los libros que pill sobre Unity, que no es que sean demasiados. Y cuando me los hube ledo todos, ya dominaba ms o menos decentemente la interfaz del engine, pero segua sin tener ni idea sobre la API de Unity. En los libros que en la actualidad hay sobre la materia la parte relativa a los scripts -que en el fondo es la ms importante- se deja en un segundo trmino. Se incluyen, un poco a boleo, varios scripts, pero de una manera totalmente desordenada y sin ningn inters pedaggico, pareciera que ms para impresionar al respetable o para copiar y pegar en otros proyectos sin saber a ciencia cierta qu se est haciendo, que para realmente explicar de dnde sale cada cosa, cules son las clases principales, su cometido y relacin de herencia entre ellas, qu demonios en un cuaternin, etc.

    La nica va para hacerse con la API de Unity pasaba pues por ir al manual de referencia de la propia pgina web del engine, la cual presenta un triple inconveniente:

    1)Est en ingls.2)Est escrito ms como manual de consulta que como instrumento de aprendizaje.3)Sigue un orden alfabtico, no un orden lgico.

    El primer inconveniente no lo fue tanto, ya que hace cuatro aos (uno antes de empezar a aprender informtica) segu al pie de la letra el consejo de un amigo:

    - Si quieres aprender a programar, antes aprende ingls.

    Y como este que os habla suple su falta de talento con una buena dosis de cabezonera, a estudiar ingls que me puse. De esta manera y gracias al consejo de mi amigo -que ruego os apliquis los que no os llevis bien con la lengua de Shakespeare- durante estas ltimas semanas he traducido la casi totalidad de las clases, estructuras y enumeraciones de Unity al castellano. No es que sea una traduccin muy pulida, aviso desde ya, pero para nuestros fines bastar.

    De los otros dos inconvenientes nos tendremos que ir ocupando poco a poco, por el viejo procedimiento de prueba y error y usando el sentido comn y la constancia donde el resto falle. Tengo el firme propsito de ir haciendo vuestros los avances que a su ver yo vaya haciendo en mi propio aprendizaje, y aunque ello entrae ir a un ritmo lento y en ocasiones nos implique tener que retroceder para revisitar algn concepto, tambin creo que puede ser interesante para alquien que empieza en esto escuchar las explicaciones y/u opiniones de otro que recin est aprendiendo tambin y que -al contrario que algunos autores con ms conocimientos- no da nada por supuesto. As que no esperis scripts de cuatro pginas (no al menos el primer ao), con todo lo bueno y lo malo que eso pueda conllevar. Y, por supuesto, sera muy positivo que hubiera la suficiente retroalimentacin con todo aqul que est interesado en hacer comn el camino de aprendizaje.

    Eso s, para no tener que hacer una parada conjunta antes de dar siquiera el primer paso, esto es, para poder seguir este "curso" (por llamarlo de alguna manera) presupongo que quien lea esto tiene una serie de conocimientos de base ya adquiridos, a saber:

    1) Algo de programacin en general, y de Javascript en particular. (Variables, funciones, bucles, condicionales, etc.)

  • 2) Conocimientos rudimentarios de POO (Programacin Orientada a Objetos)3) Haberle dado unas cuantas vueltas a la interfaz de Unity y estar familiarizado con su terminologa (gameobjects, assets, y esas cosas)

    Si no tenis esos conocimientos previos, ni podris seguir las explicaciones ni podris hacer juego alguno (desde ya les aseguro a los amigos de los atajos que no existe un software mgico que obedeciendo a tus impulsos cerebrales te construya de la nada un juego online multijugador listo para ser distribuido).

    Y sin ms prembulos, creo que podemos dar la introduccin por acabada y meternos en materia.

    1. ALGUNAS PECULIARIDADES DE UNITY

    Antes de adentrarnos en la API de Unity, conviene saber que el engine admite tres lenguajes de programacin para sus scripts: C#, Boo (una variante de Python) y algo muy parecido a Javascript, que es lo que vamos a usar nosotros en este curso.

    Javascript es de los tres lenguajes indicados el ms sencillo, y si bien algunos programadores con un cierto nivel aseguran que C# les ofrece ms control, para nuestros fines y nivel con Javascript vamos ms que sobrados.

    Una de las cosas que choca de entrada en Unity para los que tuvieran un conocimiento previo de Javascript es la forma de declarar las variables. A diferencia del Javascript standard, en Unity hemos de declarar el tipo de la variable, un poco a la antigua usanza de C y C++. Por ejemplo, para declarar la variable numero de tipo int hariamos:var numero : int;numero = 10;

    Y si la quisiramos inicializar en la declaracin, sera:var numero : int = 10;

    Esto es, palabra reservada "var" para indicar que vamos a declarar una nueva variable, el nombre que le demos a sta seguido de dos puntos y el tipo de la variable declarada.

    En otro orden de cosas, hemos de decir que la API de Unity est orientada a objetos y se compone de algo ms de un centenar de clases, de las cuales una tercera parte ms o menos estn relacionadas entre s en trminos de herencia y el resto o son conjuntos de funciones auxiliares (clase tiempo, de funciones matematicas, etc) o bien meramente permiten funcionalidades especiales no contempladas en lo que podramos llamar las clases troncales (clases relativas a la GUI o para conectarse a un servidor, por ejemplo)

    Entendiendo que las clases que estn vinculadas entre s son a su vez las que nos pueden dar una idea ms precisa del mecanismo de funcionamiento de Unity, son con vuestro permiso -y con alguna excepcin que iremos haciendo- por las que me gustara empezar. Y ya que -salvo error- nadie se ha tomado la molestia de hacer un esquema del sistema de herencia de estas clases, me tom la libertad de hacerlo yo.

    Est incompleto, ya que una veintena de clases de las que estuve mirando cuando estaba llevando a cabo la traduccin no me parecieron demasiado importantes, o por lo menos no a estas alturas, pero en esencia este sera el arbolito de clases con que nos va a tocar lidiar durante una buena temporada.

    Para agrandar la imagen pulsa AQU

    Recomiendo imprimir este esquema, ya que lo vamos a usar de referencia para, por ejemplo, saber qu funciones hereda una determinada clase, o qu clase tiene un atributo que es una instancia de otra clase. Por experiencia -poca, pero experiencia al cabo- os digo que o tenis una referencia a mano o todo os acabar sonando igual y al final os quedaris sin el que debiera ser vuestro objetivo, que no es aprenderse todas las funciones de memoria, sino entender por qu la API de Unity est estructurada de una manera y no de otra.

    Como podris observar, he incluido en el esquema las variables, funciones y funciones de clase (o atributos y mtodos,

  • si prefers la terminologa de la POO) de la clase base (Object), la principal de las que derivan de aqulla (Component) y la que a su vez hereda de sta y es heredada por otro nmero de clases. Esto nos permite, como deca antes, seguir la pista a las variables y funciones heredadas por -pongamos un ejemplo- la clase Camera, que hereda de Behaviour y por tanto tambin de Component y por ltimo de Object. As, podemos presuponer que esto es vlido: var camaraDeSeguridad : Camera;camaraDeSeguridad.name = "Mi camara";

    Esto es, nuestro objeto camera debera haber heredado una variable name de la clase Object. Teniendo a mano el esquema nos podemos evitar andar rebuscando por el manual de referencia.

    Y con esto finalizamos esta leccin. Para la prxima nos tocar ya abrir Unity.

    2. DONDE VAN LOS SCRIPTS?

    Bueno, tras un par de captulos con los prolegmenos, creo que ya podemos entrar en materia, para lo cual vamos a abrir Unity.

    Nota: En principio, y salvo que diga lo contrario, todas las caractersticas y scripts que vamos a utilizar sirven para la versin free como para la PRO, as que me da igual la versin que tengis.

    Vamos a crear un proyecto nuevo, que podis llamar como os d la gana y guardar donde queris, que para eso e ordenador es vuestro. No vamos a importar de momento ningn asset, as que dejamos desmarcadas todas las casillas y creamos el proyecto.

    Lo nico que nos aparecer en la interfaz, por tanto, ser la cmara principal. La marcamos y en el inspector, la colocamos en las siguientes coordenadas: X=0, Y=1, Z =-5.

    Ahora vamos a introducir un cubo en la escena. Nos vamos al men superior, Gameobject => Create other => Cube. Al cubo primero lo vamos a castellanizar, y en consecuencia le llamaremos Cubo. Le podemos cambiar el nombre en el inspector, o bien pulsando F2 en la jerarqua con Cube seleccionado. Vamos a ubicar nuestro cubo en las coordenadas 0,0,0.

    Vamos a salvar la escena. File =>Save Scene. Para no liarnos, recomiendo que le pongis el mismo nombre que yo, esto es, Ejemplo_1.

    Si todo ha ido bien, debis tener algo parecido a esto:

    Ahora, en la vista Project, en un espacio libre debajo de donde est la escena guardada, hacemos click derecho con el ratn, y en el men emergente seleccionamos Create => Folder. A la carpeta que aparecer le llamaremos Mis scripts. Con el puntero del ratn sobre dicha carpeta vamos a hacer de nuevo click derecho, y esta vez seleccionamos Create=>Javascript. Nos aparecer un icono representando al nuevo script con el nombre por defecto "NewBehaviourScript". Lo renombramos, llamndolo MiPrimerScript, y le damos a return para que nos conserve el cambio.

    Como podemos observar, en el inspector al seleccionar nuestro script aparece una funcin por defecto, la cual nos ser til en un buen nmero de casos. Sera lo que se conoce como una funcin sobreescribible, que quiere decir que Unity decide cundo se va a llamar (la funcin Update, que es la que sale por defecto, es llamada por Unity cada frame) y nosotros decidimos qu har cuando sea llamada, introduciendo cdigo en su interior.

    Para poder editar y/o crear nuestros scripts, hemos de acceder al editor que viene con Unity, y la mejor manera para hacerlo es hacer doble click sobre el nombre de nuestro script. Se nos abrir (en windows) esto:

  • Vamos a aprovechar que tenemos el editor abierto para explicar otra caracterstica de los scripts en Unity. Unity permite asignar/modificar los valores a las variables globales desde la propia interfaz de usuario, y concretamente desde el inspector, bien asignando esos valores "a mano", o bien arrastrando un objeto o componente del tipo de dicha variable global. Para que podamos hacer eso es preciso, como digo, que la variable tenga un mbito global, y para ello es preciso que la misma se declare fuera de cualquier funcin.

    Lo veremos mejor con un ejemplo: en el editor de scripts, encima de donde pone function Update, escribiremos lo siguiente: var camaraDeSeguridad : Camera;camaraDeSeguridad.name = "Mi camara";Debug.Log(camaraDeSeguridad.name);

    Es un miniscript parecido al que veamos en el captulo anterior. En l declaramos una variable global (est fuera de cualquier funcin) de tipo "Camera", y lo que hacemos a continuacin es -va herencia- asignarle un nombre a la variable, que ser "Mi camara". La tercera declaracin de momento nos basta con saber que muestra en pantalla (imprime) el valor de lo que est entre sus parntesis.

    Guardamos el script (si no, no funcionar) en el editor de scipts. De vuelta a la interfaz de Unity, si seleccionamos el nombre del script vemos que en el inspector se ha actualizado dicho script con el cdigo que hemos introducido.

    No obstante, nuestro script an no es funcional, ya que no lo hemos vinculado a ningn objeto de nuestra escena. Pensemos que los scripts por s slos no hacen nada, de la misma forma que cualquier asset que est en la carpeta/vista del proyecto (por ejemplo, una textura) no participar de alguna manera en una escena de nuestro juego si no lo arrastramos a esa escena, convirtindolo en parte de cualquier gameobject.

    En consecuencia, vamos a vincular nuestro script a uno de los game objects de nuestra escena, y en concreto al cubo. Para hacer eso meramente hemos de arrastrar el script desde la vista de proyecto hasta el propio objeto cubo, bien sea sobre su nombre en la jerarqua, bien sobre la propia imagen del cubo en la escena.

    Tras arrastrar el script, y con el cubo seleccionado en la jerarquia, en el inspector deberamos estar viendo lo siguiente:

    Obsrvese que en el inspector aparece ahora el nombre de nuestro script como una parte ms del Cubo que tenemos en la escena. Y daros cuenta de que la variable global queda expuesta en el inspector, de tal forma que no necesitamos ir al editor de scripts para asignarle un valor. Si la variable hubiera sido, por ejemplo, de tipo float, podramos haber introducido o cambiado su valor meramente escbibindolo en el inspector. Si en cambio la variable fuera de tipo bool, nos aparecera en el inspector con un checkbox al lado del nombre, bien marcado (true) o sin marcar (false) para que lo cambiramos a conveniencia.

    Pero como en este caso la variable es de tipo Camera, lo nico que podemos hacer para inicializarla es proveerla de un objeto de ese tipo. Dado que en nuestra escena precisamente tenemos una cmara (la main camera), meramente tendremos que arrastrarla desde la jerarqua hasta el lugar del inspector donde se consigna el valor de la variable,y que ahora mismo pone "none".

    Tras arrastrar la cmara, el inspector lucir as:

    Ya tenemos, por lo tanto, nuestro script asignado a un gameobject (nuestro cubo) y la nica variable global inicializada con otro object (la cmara principal). Procedamos ahora a darle al play y ver qu sucede.

    Si nos fijamos, debajo de la ventana game nos aparecer impreso el nombre que le asignamos a nuestra cmara.

    Antes de acabar me gustara que practicramos un pequeo ejercicio que nos permitir comprender las diferentes

  • maneras (correctas e incorrectas) en que se puede declarar una variable. Borremos todo el contenido de nuestro script MiPrimerScript y tecleemos lo siguiente:var sinTipoNiIni;var conTipoSinIni : int;var sinTipoConIni = 10;var conTipoConIni : int = 10;var aPlazos : int;aPlazos = 10;private var miSecreto : int;var arrastrame : GameObject;function Update() {var enLaFuncion : int;}

    Salvamos el script y, con el Cubo seleccionado (recordemos que este script est todava vinculado al cubo que tenemos en la escena) echamos un vistazo al inspector. Podemos de manera emprica llegar a las siguientes conclusiones para cada manera de declarar una variable:

    La variable sinTipoNiIni, que como su nombre apunta hemos declarado sin indicar ni el tipo de datos que debera contener, ni la hemos inicializado conjuntamente con la declaracin, no aparece en el inspector, porque ste no tiene forma de saber qu tipo de variable estamos declarando.

    La variable conTipoSinIni s es tenida en cuenta por el inspector, ya que aunque no la hemos inicializado, s que hemos indicado el tipo de dato que queremos almacenar en ella.

    La variable sinTipoConIni s aparece en el inspector, ya que aunque no hemos declarado el tipo que contendr, al estar inicializada permitimos a Unity deducir en base al valor en tipo de variable apropiado. Fijmonos que adems de aparecer la variable en el inspector, lo hace con el valor inicial que le hemos dado.

    La variable conTipoConIni aparece obviamente en el inspector.

    La variable aPlazos aparece en el inspector, pero sin inicializar. En cambio, si pulsamos play observaremos que automticamente le es asignado el valor 10 que le dimos en segunda declaracin.

    La variable miSecreto no es accesible desde el inspector porque es privada. De hecho, esa variable no podr se accedida desde ningn script distinto del que la contiene.

    La variable arrastrame es recogida en el inspector, y al ser de un tipo propio de Unity, nos aparecer con una flechita diminuta en la derecha que nos indica que podemos importar del juego cualquier componente de ese tipo para asignar un valor a la misma, bien usando el men que emerge al clickar dicha flecha, bien va arrastrar el objeto o componente desde las carpetas de proyecto o jerarqua.

    Por ltimo, la variable enLaFuncin no aparece en el inspector, ya que al ser declarada dentro de una funcin no tiene carcter pblico.

    Bueno, pues con esto ya debera quedar un poco ms clara la manera en que las variables y los scripts son tratados en Unity. Para la prxima leccin empezaremos a analizar clase por clase de la API.

    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 ms conveniente empezar con la clase base.

  • Es sabido que en POO las clases base de las que heredan las dems suelen tener una funcionalidad bastante limitada, cuando no inexistente, siendo su funcin ms la de punto de partida de las que habrn 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 excepcin a esta regla.

    La clase Object consta de dos variables, dos funciones y nueve funciones de clase (FC). Como sabris, las funciones de clase se diferencian de las funciones standard en que aqullas 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 conocamos. 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 opcin con un ejemplo muy sencillo. Abrimos Unity y nos vamos a la escena que creamos en la leccin 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 leccin anterior, Debug.Log nos permite imprimir en pantalla el valor de lo que se halle entre parntesis. Salvamos el script (pero no cerramos el editor, meramente lo minimizamos)

    Hay una cuestin 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 automticamente se modifican todas las copias de este script que tengamos por ah. En el caso que nos ocupa, recordaremos que a nuestro cubo le habamos aadido una copia de este script, que a estas alturas por lo expuesto ya habr sido modificado.

    Probmoslo. 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 definicin 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 habamos escrito en el editor de scripts entre parntesis 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 conseguira con su manipulacin hacer que un determinado objeto desaparezca o bien del inspector, o bien de la jerarqua, o impedir que dicho objeto se pueda editar, o no permitir que un objeto sea salvado con la escena y en consecuencia destrudo 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

  • enumeracin, de nombre HideFlags, tiene las siguientes opciones:

    HideInHierarchy____El objeto no aparecer en la jerarqua. 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___Combinacin de HideInHierarchy y DontSave.

    As, si por ejemplo quisiramos (no se me ocurre muy bien para qu) que el cubo desapareciera del inspector mientras se desarrolla el juego, le vincularamos un script parecido a este: var meEvaporo: GameObject;meEvaporo.hideFlags = HideFlags.HideInInspector;

    Este script lo podramos arrastrar a la cmara 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 debera 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 deca al principio de la leccin 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.

    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 funcin devuelve ese identificador.

    Vamos a utilizar estas dos funciones en un mismo ejemplo. En el captulo anterior tenamos vinculado nuestro script-para-todo a la cmara 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 explicacin. Asegurmonos 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 mramente 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 sera 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 funcin lo que hace es clonar el objeto que le pasamos como primer parmetro, y devolver ese clon del objeto original, ubicndolo en posicin y rotacin determinadas.

    Observamos que el parmetro "position" es de tipo Vector3, que es una clase que no tardaremos mucho en estudiar. De hecho, posiblemente sea la prxima que tratemos, ya que aunque no pertenece a la jerarqua de herencia que hemos tomado como referencia para el orden de estudio, s que va a aparecer el nmero suficiente de veces durante el trnsito por dicha jerarqua 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-detrs). 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, escribiramos en nuestro Vector3 (1,0,0).

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

    El otro parmetro de la funcin, rotation, es de tipo Quaternion. Quien sepa lo que es, felicidades, yo estuve varios das pelendome con apuntes de matemticas por la red y a lo sumo me hice una idea abstracta de que un cuaternin se compone de tres nmeros reales y uno imaginario, el clculo de los cuales establece la rotacin 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 cuaternin 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 cmara principal el script que le habamos colocado anteriormente. Para hacer eso, con la cmara seleccionada en el inspector colocamos el ratn sobre el nombre del script, botn 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 cmara), vamos a crear un gameobject vaco 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 jerarqua 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 funcin 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 rotacin 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 rotacin que el objeto padre, pero esto ya lo explicaremos cuando toque.

    Salvamos el script y lo arrastramos hasta nuestro PortaScripts en la jerarqua. 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. Debera aparecernos un segundo cubo, tal que as:

    Podemos observar alguna cosa ms. En la jerarqua 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 habamos indicado.

    Probemos otra cosa. Detenemos la reproduccin 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. Dmosle al play.

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

    La funcin instantiate es muy usada para crear proyectiles, partculas en explosiones e incluso AI (inteligencia artificial) para enemigos.

    Cabe una segunda forma distinta para la funcin 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 rotacin que el original. Podemos probarlo en nuestro script meramente eliminando el segundo y tercer parmetro. Al darle al play, sabemos que ha aparecido un clon porque as lo indica la jerarqua, pero no podemos distinguirlo porque est justo en el mismo sitio que el original y se estn solapando.

    Destroy:

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

    Como su nombre indica, esta funcin borra del juego un gameobject, un componente o un asset. Tiene dos parmetros, siendo el primero el elemento a borrar y el segundo el tiempo en segundos que tardar en borrarlo desde que se llame a la funcin (si no se indica lo contrario, por defecto el parmetro indica cero segundos, esto es, la destruccin del objeto es automtica).

    Hay una cuestin que igual ahora no se entiende muy bien, pero la dejo apuntada para tratarla ms en profundidad en otro momento: si en el parmetro obt colocamos un Componente, la funcin slo eliminar ese componente, hacindolo desaparecer del Gameobject al que pertenezca. Pero si en cambio lo que le pasamos a la funcin 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 relacin de parentesco). Para tener un acercamiento intuitivo a esta relacin 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 funcin destroy, slo se destruir la rueda. Si en cambio lo que destruye es el gameobject coche, se destruir tanto el vehculo como las ruedas como el conductor.

  • Vamos a probar la funcin Destroy. Para empezar devolvamos a nuestro cubo original a su rotacin original (0,0,0). Vamos a rehacer ahora nuestro sufrido script, que tenemos vinculado al cubo. En el editor de scripts modificamos nuestro cdigo 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 fcil de entender. Lo que hemos hecho es aadir 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 funcin Instantiate. Inmediatamente es llamada la funcin Destroy, que borrar el elemento clonado que hemos almacenado en nasioPaMorir pasados diez segundos.

    Salvamos, le damos al play, contamos diez, y adis clon.

    DestroyImmediate:

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

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

    Por lo tanto, olvidmonos de DestroyImmediate.

    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 funcin, como sucedera en una funcin normal (o mtodo) dentro de una clase.

    FindObjectOfType

    static function FindObjectOfType (type : Type) : Object

    Esta funcin devuelve el primer objeto activo que Unity encuentre que sea del tipo que le pasamos como parmetro.

    Vemoslo en un ejemplo. Recuperamos una vez ms 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 cmara principal.

    Nos advierte el manual de referencia que esta funcin puede ser un poco lenta y costosa en trminos de rendimiento, por lo que se recomienda no usarla a su vez como parte de una funcin que se actualice cada frame (como por ejemplo la funcin 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 funcin es idntica 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 despus una variable de tipo string para que vaya recopilando toda la informacin 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 aadiendo a nuestro string "chivato" el nombre de todos los objetos encontrados. Finalmente, imprimimos. Si todo ha salido bien, el resultado tendra que ser este al darle al play: DontDestroyOnLoad static function DontDestroyOnLoad (target : Object) : void Con esta funcin conseguimos que el objeto que colocamos como parmetro 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 funcin, conseguimos que los objetos que queramos pervivan al cambio de escena.

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

    Y con esto acabamos el anlisis de nuestra primera clase. Espero que os hayan quedado los conceptos ms o menos claros (al principio cuesta un poquillo, as que no desesperis). Y por supuesto, si hay alguna duda, queja (educada) o correccin que hacer, sentiros libres de comentar.

    6. ESTRUCTURA VECTOR3 (I)

    Lo primero que cabra decir de Vector3 es que, a diferencia de Object, por ejemplo, no es en s una clase, sino una estructura. En la corta historia de la programacin las estructuras surgieron antes de las clases, en los viejos tiempos de

    la programacin procedimental, y originalmente eran poco ms que una coleccin de variables que guardaban algn tipo de relacin entre s. Hoy por hoy, no obstante, no hay ninguna diferencia entre una estructura y una clase.

    Aunque Vector3 no est contenido dentro del esquema de herencia principal de Unity, es una clase imprescindible para la creacin de cualquier juego, ya que precisamente es la que permite que representemos puntos y vectores en el

    espacio 3D, y por ende la que nos permite trabajar con distancias y desplazamientos.

    El concepto de punto, siendo sencillo cuando se entiende, a veces provoca en los recin llegados no pocas confusiones. Quizs lo mejor para captar el concepto de punto en 3D previamente tengamos que aprehender el

    concepto de punto en 2D. Vamos a intentar explicarlo de una manera llana, y ruego me disculpen los que ya tenan claro el concepto:

    Delante vuestro posiblemente tengis la pantalla de vuestro ordenador o del dispositivo electrnico desde el que estis

  • viendo estas lneas. En algn lugar de la pantalla estar seguramente el cursor del ratn, apuntando a algn punto (valga la redundancia) determinado de sta. Si quisiramos explicarle a alguien que no est viendo la pantalla en qu

    lugar est el cursor, o si meramente quisiramos dejar por escrito de una manera fiable en qu punto exacto est ahora nuestro cursor, una buena manera de hacerlo sera decir: el cursor est X centmetros (digamos por ejemplo 16) a la

    derecha del borde izquierdo de la pantalla e Y centmetros por debajo del borde superior de la pantalla (pongamos 20). As, si por ejemplo convenimos que la esquina superior izquierda de la pantalla es el punto 0,0, nuestro cursor en el

    mundo 2D de la pantalla estar situado en el punto 16,20.

    Por lo tanto, observamos que para determinar la situacin exacta de un punto necesitamos a su vez otro punto que nos sirva de referencia (Y es importante retener este concepto, sobre el que no tardaremos en volver). As, para saber

    dnde est el punto al que seala el ratn necesitamos primero compararlo con el punto que tomamos como referencia, y la resta entre ambos puntos es la distancia que los separa, que es este caso ya no sera un punto sino un vector,

    aunque se represente igual.

    Lo mismo es aplicable a las tres dimensiones, aunque a nivel intuitivo nos cueste un poco ms de pillarlo. Meramente entendamos que si nuestra pantalla de ordenador no fuera plana, sino por ejemplo un holograma, para localizar el

    objetivo de nuestro ratn tendramos que aadir un nuevo punto (o eje) Z, para calcular la profundidad con relacin a un punto de origen (que podra ser la esquina superior izquierda ms cercana a nosotros de la pantalla hologrfica en 3d)

    La estructura Vector3, pues, nos permite averiguar en qu punto se halla cada gameobject, cambiar su posicin, calcular las distancias entre ellos, etc.

    Es conveniente advertir que el tipo Vector3 no es la nica clase/estructura que maneja lo relativo a puntos y desplazamientos. Cumplen funcionalidades parecidas (que no iguales) la clase Quaternion y la Matrix4X4. Existen,

    antes de que nos alarmemos en exceso, funciones que permiten convertir, por ejemplo, un Vector3 en un Quaternion (y viceversa), as que de momento no nos obsesionemos ms de la cuenta con conceptos que nos suenen extraos.

    Y tras esta extensa introduccin, en el prximo captulo empezamos con la estructura en s.

    7. ESTRUCTURA VECTOR3 (II)

    CONSTRUCTOR:

    1)static function Vector3 (x : float, y : float, z : float) : Vector3

    Como sabemos de nuestros conocimientos de programacin orientada a objetos, el constructor es una funcin especial cuyo cometido es crear de manera adecuada las instancias/objetos de una determinada clase o estructura. La funcin

    constructora se caracteriza por tener el mismo nombre que la clase.

    Tal como vemos en el prototipo de nuestro constructor, le hemos de pasar al mismo los tres componentes de nuestro punto 3D, referidos al eje horizontal, vertical y de profundidad respectivamente, en formato de punto flotante. As:

    var miPunto = Vector3(2.0, 5.0, 4.8);

    referenciamos un punto que con respecto al origen se desplazar dos unidades a la derecha, cinco hacia arriba y 4,8 hacia el fondo.

    Vale, pero cul es el origen?, se preguntar alguien.

    Pues depende, tendremos que responder. Si un objeto no depende de otro, entendemos por origen el punto 0,0,0 de las coordenadas globales (que es donde deberamos tener nuestro cubo) y desde ese punto origen se calcula el desplazamiento, pero si un objeto depende de otro (recordemos el ejemplo de la rueda y el coche) caben para ese objeto dos orgenes o coordenadas diferentes: las globales (distancia de la rueda respecto del eje 0,0,0 del caso anterior) y las locales (referidas al objeto del que depende)

    2)static function Vector3 (x : float, y : float) : Vector3

  • Como vemos, hay una segunda funcin constructora que no toma en cuenta el eje Z (la profundidad) asumiendo que sta es cero. Por tanto, si a la funcin constructora Vector3 slo le pasamos dos parmetros, automticamente ser llamada esta segunda versin del constructor.

    VARIABLES:

    x, y, z:

    Estas variables se refieren a los componentes X, Y y Z del vector, respectivamente.

    this:

    var this[index : int] : float

    Esta es otra forma para acceder a los componentes X, Y y Z del vector, siendo los respectivos ndices de cada componente 0, 1 y 2.

    As, estas dos declaraciones son equivalentes: var miVector : Vector3;miVector.x = 3.0;miVector[0] = 3.0; //Equivale a la anterior

    magnitude:

    var magnitude : float

    Esta variable devuelve la longitud o tamao de un vector. Habamos hablado previamente de que un vector3 contiene el nmero de unidades que lo separan del origen dado en los ejes X, Y y Z. Obviamente, cuanta mayor es la distancia entre nuestro Vector3 y el punto de origen, mayor la longitud del vector.

    La longitud del vector equivale a la raiz cuadrada de (x*x+y*y+z*z).

    Soy consciente de que a estas alturas la utilidad de funciones como estas es difcil de apreciar. Con un poco de paciencia, y cuando las combinemos con otras clases, veremos el partido que se le puede sacar a todo esto.

    sqrMagnitude:

    var sqrMagnitude : float

    Si de lo que se trata es de comparar dos vectores, es preferible usar esta funcin que la precedente, ya que aunque parezca antiintuitivo, Unity calcula con ms rapidez los cuadrados de las magnitudes que las magnitudes en s.

    normalized:

    var normalized : Vector3

    Esta variable devuelve un vector con la misma direccin que el original, pero con magnitud 1. Es importante constatar

  • que el vector original permanece inalterado y lo que se devuelve es meramente una copia que conserva la direccin de dicho original, aunque con la longitud alterada. Si en lugar de normalizar una copia quisiramos hacer lo propio con el original, entonces tendramos que usar la funcin Normalize, que veremos en breve.

    Decir tambin que si el vector es demasiado pequeo para ser normalizado, lo que se devuelve es un vector a cero.

    VARIABLES DE CLASE:

    zero:

    static var zero : Vector3

    Es meramente un atajo para poner un vector a cero.transform.position = Vector3.zero; // Es lo mismo que escribirtransform.position = Vector3(0,0,0);

    one, forward, up y right:

    Son respectivamente atajos para escribir Vector3(1,1,1), Vector3(0,0,1), Vector3(0,1,0) y Vector3(1,0,0).

    Vamos a ver algunos de los conceptos de esta leccin en un ejemplo. Abrimos nuestro script favorito y escribimos: var unObjeto : GameObject;var desplazamiento : Vector3;var magnitud : int = 0;desplazamiento = Vector3.right;for(var contador = 0; contador < 3; contador++){ unObjeto.transform.position += desplazamiento; magnitud += desplazamiento.magnitude; Debug.Log("Longitud del Vector del cubo: " + magnitud); }

    Salvamos, arrastramos nuestro cubo para que se convierta en la variable unObjeto y le damos al play. Nuestro cubo se mueve tres unidades a la derecha y bajo la ventana Game aparece la magnitud de desplazamiento del vector.

    El script funciona de la siguiente forma: Adems de la variable que sostendr a nuestro cubo, creamos una variable de tipo Vecto3 para poder almacenar a modo de impulsos maniobras de desplazamiento de una unidad a la derecha. Ese input a la derecha se lo daremos tres veces mediante un bucle for, y cada magnitud individual (que obviamente vale 1 cada vez) se acumula en una variable de tipo int que hemos creado a tal efecto.

    8. ESTRUCTURA VECTOR3(III)

  • Scale:

    function Scale (scale : Vector3) : void

    Multiplica el Vector3 que llama la funcin por el Vector3 que le pasamos a dicha funcin como parmetro.

    Lo veremos mejor con un ejemplo. Vamos a darle un meneo de nuevo a nuestro script. Tecleamos: var unObjeto : GameObject;var miVector : Vector3;miVector = unObjeto.transform.position = Vector3(2.0, 1.0, 3.0); Debug.Log("Magnitud inicial del cubo " +miVector.magnitude);miVector.Scale(Vector3(3.0, 1.5, 2.0));unObjeto.transform.position = miVector;Debug.Log("Magnitud final del cubo " +miVector.magnitude);

    Antes que nada vamos a explicar lo que pretendemos hacer: dejamos expuesta la variable unObjeto para arrastrar con posterioridad nuestro cubo desde el inspector. Asimismo declaramos una variable para sostener el valor original y modificado para un vector3.

    Aunque an no lo hemos estudiado, a nadie sorprender que los diferentes gameobjects que ocupan la escena han de ir ineludiblemente vinculados a un objeto transform, el cual a su vez es el que sirve para fijar en qu punto del espacio 3D se halla el gameobject. As, para obtener o indicar la posicin de un gameobject en el espacio usaramos la sintaxis gameobject.transform.position. Se da la circunstancia que la variable position de la clase transform es de tipo Vector3, por lo que nos sirve para nuestro ejemplo.

    Hacemos una iniciacin doble, de tal manera que damos unos valores a nuestro Vector3 miVector, que a su vez tendr el mismo valor que le damos a la posicin actual del cubo. Obtenemos la magnitud de dicho vector y la imprimimos.

    Luego usamos la funcin Scale, y la magnitud del vector modificado es impreso de nuevo, y a la par el cubo se desplaza por segunda vez.

    Obtendremos al darle al play un resultado como este

    Si nos fijamos en la parte inferior de la ventana Game, slo aparece impresa la magnitud final del vector de nuestro cubo. Para ver tambin el valor inicial, hemos de hacer un click sobre dicho texto impreso, de tal suerte que nos aparecer una ventana emergente con ambos valores, tal como mostramos:

    La funcin Scale tiene un segundo prototipo, que es el que sigue

    static function Scale (a : Vector3, b : Vector3) : Vector3

    El fin ltimo es, al igual que en la funcin homnima, multiplicar dos vectores. La diferencia es que aqu pasamos expresamente los dos vectores que queremos multiplicar entre s. Tambin difieren ambas funciones en que la primera no devuelve ningn valor (el cambio resultante de la multiplicacin es directamente asignado al objeto que la llama) y esta en cambio s devuelve un Vector3 con el resultado, que podemos por lo tanto asignar a una variable.

    Normalize:

    function Normalize () : void

  • Esta funcin hace que el Vector3 que la llama pase a tener una magnitude de 1, conservando, eso s, la misma direccin. Como decimos, esta funcin cambia el vector que la llama; si lo que pretendemos es conservar sin cambios el vector corriente y crear un nuevo vector normalizado, hemos de usar -como ya aprendimos en el captulo anterior- la variable normalized.

    ToString:

    function ToString () : String

    Devuelve nuestro vector convertido en un string y formateado.

    Para ver como funciona, aadamos al final del script que usamos en el ejemplo anterior la siguiente lnea: Debug.Log("Mi vector como string formateado: " + miVector.ToString());

    Veremos que nos aparece el vector resultado de la multiplicacin del anterior ejemplo convertido en un string que podemos imprimir.

    Dejamos para el prximo captulo las dos funciones de clase que nos restan para acabar la explicacin de la estructura Vector3.

    9. ESTRUCTURA VECTOR3 (y IV)

    FUNCIONES DE CLASE:

    Lerp:

    static function Lerp (from : Vector3, to : Vector3, t : float) : Vector3

    Esta funcin interpola linealmente entre dos vectores, desde from hacia to en el valor t.

    Vamos a intentar explicarlo de la manera ms clara posible, ya que se trata de una funcin importante: Lerp realiza una lnea imaginaria que va del punto que le pasamos como primer parmetro (from) al punto que le pasamos como segundo parmetro (to). Esa lnea imaginaria es entonces una recta con un origen y un final, y entre ambos extremos tiene muchos puntos intermedios. Pensemos por ejemplo en una regla, para tener ms o menos claro el concepto. Pongamos que al punto de origen (from) le asignamos el valor 0, y al punto final (to) le damos el valor 1. En ese caso, el punto que est en la mitad de nuestra recta imaginaria es evidente que valdr 0,5.

    Con esto claro planteemos ahora el mismo asunto pero al revs. Tenemos el punto origen y el punto final y queremos saber dnde est la mitad exacta entre esos dos puntos. Lo que haramos es llamar a la funcin Lerp, asignar a los parmetros primero y segundo respectivamente las coordenadas de origen y final y darle al tercer parmetro (que en el prototipo recibe el nombre de "t", y es de tipo float) el valor 0.5. El valor retornado por la funcin se corresponder con el punto situado en el centro de los dos extremos dados.

    El parmetro t puede recibir valores entre 0.0 (que coincide con el origen) hasta 1.0 (que coincide con el final).

    Vamos a practicar.

  • En Unity, le quitamos provisionalmente a nuestro gameobject PortaScripts el script, para que no nos d errores en lo que a continuacin haremos.

    Acto seguido vamos al men gameobject=> creater other => capsule. Colocamos la cpsula a un valor -3 en el eje x del transform en el inspector. Renombramos a esta cpsula y la llamamos Origen.

    Creamos luego otra cpsula, y en la coordenada x de su transform la pondremos en 3. La rebautizamos como Fin (Final es una palabra reservada)

    Es preferible que tengamos la escena enfocada desde la vista superior (lo que conseguimos haciendo click en el eje Y del gizmo)

    Ahora hacemos doble click en el script (que aunque hemos borrado del Portascripts sigue estando en el Proyecto), y tecleamos: var principio : Transform;var fin : Transform;function Update () {transform.position = Vector3.Lerp(principio.position, fin.position, 0.3);}

    Salvamos el script y lo arrastramos a nuestro cubo. Acto seguido seleccionamos el cubo y en el inspector nos aparecern nuestras dos variables. Arrastramos origen a principio y destino a fin.

    Si todo va bien, nuestro cubo debiera situarse a tres dcimas partes de la lnea hipottica que separa las cpsulas, contando desde la que hemos dado en llamar origen. Lo probamos?

    Bien. Parece que funciona, pero podemos ir un poco ms all. Para ello vamos a recurrir a una clase que an no hemos estudiado, que es la clase Time, y concretamente a la variable time de la clase Time. Time.time es una variable que representa los segundos transcurridos desde que el juego se inicia. Por lo tanto, la variable vale cero al iniciarse el juego, 1 un segundo despus, etc.

    Con esto en mente, vamos a nuestro ltimo script y dentro de la funcin Lerp sustituimos el 0.3 por Time.time, salvamos y le damos al play.

    Efectivamente, nuestro cubo empieza pegado al origen (el parmetro t, representado por Time.time vale cero) y va desplazndose hacia el fin hasta que t vale 1, o sea, hasta que transcurre un segundo.

    Sabiendo esto, podemos hacer que el trayecto de nuestro cubo dure por ejemplo diez segundos en lugar de uno. Conseguimos esto meramente multiplicando Time.time * 0.1.

    Podemos, dndole una vuelta ms de tuerca al asunto, crear una variable expuesta (susceptible de ser accesada desde el inspector) de tipo float, que el usuario pueda modificar, y que al ser multiplicada por Time.time haga que nuestro cubo viaje ms rpido o ms lento.

    Slerp:

    static function Slerp (from : Vector3, to : Vector3, t : float) : Vector3

    Esta funcin es similar a la anterior, con la particularidad de que en lugar de interpolar en lnea recta lo hace en forma esfrica. Para probarla no tienes ms que sustituir el trmino "Lerp" del script anterior por "Slerp", y poner el cubo en rbita. Si tienes tiempo y ganas, ubica los gameobjects Origen y Destino en otras posiciones a travs de sus respectivos transforms en el inspector (desplzalos tambin en sus ejes Y y Z).

  • 10. CLASE TRANSFORM (I)

    Vamos a dedicar los prximos captulos a estudiar una de las clases ms importantes de la API de Unity. La clase Transform, como vemos en el grfico, hereda de Component, que a su vez deriva de Object, y por lo tanto esta clase

    tiene como propias las variables y funciones de las dos clases de las que hereda.

    La pregunta parece obligada: si Transform hereda de Component, por qu no estudiamos la clase Component primero?

    No lo hacemos porque la clase Component, en el fondo, es poco ms que una recopilacin del resto de clases. Lo veremos mejor si recuperamos el grfico general de clases que vimos en su da:

    Si nos fijamos con atencin, observaremos que la mayora de las variables de la Clase component son a su vez instancias (objetos) de un buen nmero de las otras clases, algunas de las cuales incluso -como la propia clase

    Transform- derivan de sta. Nos veramos pues, caso de estudiar primero la clase Component, obligados a explicar lo que es un transform, un rigidbody, una camera, etc, antes de explicar las clases que los desarrollan.

    Por lo tanto, para no liarnos en exceso, dejaremos la clase Component para bastante ms adelante.

    Bien. Qu es un transform?.

    Posiblemente muchos de los que ya hayan trasteado con la interface de Unity asimilarn esta palabra a la seccin homnima del inspector. Efectivamente, si seleccionamos cualquier gameobject de la escena, automticamente el

    inspector nos muestra en la seccin transform los valores de posicin, rotacin y escala de dicho gameobject.

    No puede existir un gameobject en la escena sin su propio transform. Si nos fijamos, hasta nuestro PortaScripts tiene vinculado un transform, pese a que como mero contenedor de scripts no es renderizado en la escena, y por lo tanto es

    invisible. En consecuencia, todo gameobject en nuestra escena est situado en un lugar determinado de sta (position), con una determinada inclinacin (rotation) y le es aplicada una determinada escala comparativa de tamao (scale).

    Con esto en mente, empecemos.

    VARIABLES:

    position:

    var position : Vector3

    Esta variable nos indica en qu punto del espacio global se ubica nuestro transform (y por ende el gameobject al que el mismo va vinculado). Recordemos que el espacio global es el que hace referencia a las coordenadas de la escena, y no a las de un determinado objeto. Si quisiramos consultar/indicar el lugar del espacio local de un objeto, utilizaramos la

    variable localPosition.

    La variable position es de tipo Vector3, y nos permite tanto obtener los datos de posicin del transform como modificarlos para darle una nueva ubicacin. Por ejemplo, si quisiramos ubicar un objeto en el centro del espacio

    global, haramos: elObjetoQueSea.transform.position = Vector3(0, 0, 0);

    Podemos asimismo acceder a uno slo de los ejes de la posicin, tal como sigue: Debug.Log(elObjetoQueSea.transform.position.x);

  • Lo cual nos permitira imprimir la posicin que tiene nuestro transform referida al eje horizontal.

    localPosition:

    var localPosition : Vector3

    Como adelantbamos hace un momento, localPosition indica la posicin del transform al que dicha variable pertenece, calculada en trminos relativos al transform del que aqul depende.

    Parece ms difcil de lo que es en realidad, tal como vamos a demostrar con un ejemplo sencillo:

    Nos vamos a Unity. Si hemos guardado nuestra ltima sesin, debemos tener en nuestra escena, aparte de nuestro viejo amigo el cubo, dos cpsulas de nombre origen y destino. Eliminamos Destino. Si por lo que sea no hubiramos conservado las cpsulas, creamos un gameobject capsule nueva. Esa cpsula (u Origen, si guardamos la ltima escena)la ubicamos desde el inspector en la posicin (3, 4, 2)

    Resumiendo, tenemos en la escena(o debiramos tener) el cubo en las coordenadas de posicin (0,0,0) y una cpsula en la posicin (3,4,2). Ninguno de estos gameobjects es hijo a su vez de otro, por lo que estn y se mueven en coordenadas globales.

    Abrimos nuestro script y escribimos: transform.position = Vector3(-2,-1,-1);

    Salvamos y arrastramos el script a nuestro cubo. Al darle al play, vemos que ste se desplaza respecto la posicin que ocupaba en las coordenadas globales (el centro justo) dos unidades a la izquierda, una hacia abajo y una hacia delante. Al darle al pause, nuestro cubo vuelve a su lugar original, en 0,0,0 (es importante retener esto)

    Vamos ahora a, en la Jerarqua, arrastrar el gameobject cubo y soltarlo dentro de gameobject cpsula (u Origen). Vemos que el cubo mantiene su posicin en la vista de escena, pero si lo seleccionamos (est ahora contenido en la cpsula), sus coordenadas en el transform del inspector son ahora las inversas que las que le dimos al gameobject cpsula. Esto es as porque ahora para el cubo sus coordenadas de posicin no dependen ya del espacio global, sino del local de la cpsula de la cual depende. Para el cubo la coordenada 0,0,0 no es ya el centro de la escena, sino el centro del gameobject capsula, y dado que con respecto a la cpsula el cubo est desplazado tres unidades a la izquierda, cuatro hacia abajo y dos hacia delante, son las que muestra.

    El hecho de que el cubo dependa de la cpsula o dicho en terminologa Unity, el cubo sea hijo de la cpsula, no implica que no pueda seguir siendo localizado/ubicado en coordenadas globales. De hecho, si le damos al play de nuevo, habida cuenta de que estamos utilizando en nuestro script la variable position (global), el cubo se seguir desplazando al mismo sitio al que lo haca cuando an no dependa de la cpsula.

    Pero, obviamente, al tener ahora un padre, le podemos aplicar ahora al cubo la variable localPosition, y as podemos modificar el script como sigue: transform.localPosition = Vector3(-2,-1,-1);

    Vemos ahora que el movimiento del cubo lo es en relacin con la posicin que ocupa pap capsula (o para ser ms precisos el transform de pap cpsula). Probad si no lo veis claro a pasarle un vector (0,0,0) a nuestro script en lugar del que hemos escrito antes.

    Si le aplicramos esta variable localPosition a un transform sin padre, dicho transform se movera en el espacio global, tal como si le hubiramos aplicado position y no localPosition.

    11. CLASE TRANSFORM (II)

  • eulerAngles:

    var eulerAngles : Vector3

    Esta variable indica la rotacin del transform en grados. Se corresponde plenamente con los valores que aparecen en el apartado rotation de cada transform en el inspector. Los valores de esta variable vienen dados, al igual que los de position, en formato Vector3. Lo que sucede es que un valor de (90,0,0) referido a la posicin implica que nuestro

    transform se desplace 90 unidades a la derecha, mientras que los mismos valores relativos a la rotacin implicaran que nuestro transform gire 90 grados (el equivalente a un cuarto de vuelta) sobre el eje X.

    Al igual que con la variable position, eulerAngles nos permite consultar individualmente la rotacin de nuestro transform sobre un determinado eje. Pero si lo que queremos no es consultar sino cambiar la rotacin, deberemos indicar los tres

    valores va un Vector3, y Unity los convertir entonces en los valores que l usa internamente.

    Porque -esto no s si lo he dicho antes- Unity trabaja internamente con Quaterniones, por motivos de rendimiento y exactitud, mientras que a los humanos nos resulta ms sencillo hacerlo con grados Euler, as que en la prctica esisten

    unos "atributos de traduccin" que nos permite pasar de quaterniones a grados y viceversa, a fin de que hombres y mquina sigan trabajando con los elementos que mejor se adapten a sus capacidades.

    Al igual que con position, existe una variable eulerAngles para funcionar con valores globales (la que estamos estudiando) y otra localEulerAngles que trabaja con valores locales (dependientes del padre del transform que rota)

    A esta variable slo podemos asignarle valores exactos, no de incremento, ya que fallara pasados los 360. Para incrementar una rotacin de esta forma, tenemos que usar la funcin Transform.rotate.

    Usemos un ejemplo muy simple. Antes arrastremos nuestro cubo en la jerarqua fuera de la cpsula, para desemparentarlo. Y rehacemos por ensima vez nuestro script (que debera continua vinculado al cubo)

    transform.eulerAngles = Vector3(60,0,0);

    Podis ver que nuestro cubo ha girado un 60 grados sobre el eje X, entendido ste como un alambre invisible que cruza de manera horizontal la escena (por decirlo de alguna manera)

    Como colofn, aadir que Unity automticamente convierte los angulos Euler que le pasamos al transform en los valores usados para la rotacin almacenada en Transform.rotation.

    localEulerAngles:

    var localEulerAngles : Vector3

    Aqu no deberamos necesitar grandes explicaciones: localEulerAngles se refiere a la rotacin en grados de un transform con relacin a la que tiene el transform del cual depende en una relacin hijo/padre.

    Al igual que con la variable anterior, Unity automticamente convierte los angulos Euler en los valores usados para la rotacin almacenada en Transform.localRotation.

    rotation:

    var rotation : Quaternion

    Es la variable que contiene la rotacin del transform en el mundo global almacenada en quaterniones.

  • Como ya hemos dicho, Unity almacena internamente todas las rotaciones en quaterniones, lo cual no quiere decir que nosotros tengamos que trabajar directamente con ellos. Por norma general, o bien trabajaremos con grados Euler que luego se convertirn implcita o explicitamente en quaterniones, o bien a lo sumo usaremos alguna funcin que nos permita encapsular las operaciones con quaterniones.

    localRotation:

    var localRotation : Quaternion

    Obviamente, esta variable indica la rotacin en quaterniones de un transform relativa a la rotacin de su padre

    right, up y forward:

    var right : Vector3 var up : Vector3 var forward : Vector3

    Estas variables hacen referencia respectivamente al eje X(derecha/izquierda), Y (arriba/abajo) y Z(delante/detrs) de las coordenadas globales del transform. No las hemos de confundir con las variables del mismo nombre de la estructura Vector3. As, Vector3.up es un atajo que equivale a Vector3(0,1,0), esto es, implica un movimiento de una unidad hacia arriba. En cambio, transform.up meramente hace referencia al eje arriba/abajo, pero no implica movimiento. Para mover un transform por esta va tendramos que hacer algo como esto:

    En Unity eliminamos el script que tenemos vinculado a nuestro cubo. En Proyecto abrimos nuestro script y tecleamos: var unObjeto : GameObject;unObjeto.transform.position = transform.right*10;unObjeto.transform.eulerAngles = transform.up*45;

    Arrastramos nuestro script tras salvarlo a PortaScripts en la Jerarqua y con Portascripts seleccionado arrastramos el cubo a la variable expuesta. Play.

    Efectivamente, el cubo se desplaza 10 unidades a la derecha y 45 grados sobre el eje Y.

    12. CLASE TRANSFORM (III)

    Vamos a explicar las variables que nos quedan en esta leccin.

    localScale:

    var localScale : Vector3

    Como su nombre indica, es una variable de tipo Vector3 que permite consultar o establecer la escala de un transform en relacin con la de su padre. Obviamente, un transform con esta variable en 0,0,0 tendr la misma escala que su padre.

  • parent:

    var parent : Transform

    Hace referencia al padre del transform (que es obviamente otro transform), y a travs de esta variable se puede consultar y/o cambiar dicho padre.

    Veamos primero de qu forma podemos consultar valores relativos al padre de nuestro transform: Nos vamos a Unity, arrastramos en la Jerarqua el cubo dentro de la cpsula, y a PortaScripts le borramos el script para que no nos d errores. En Proyecto le damos doble click a miPrimerScript y escribimos lo que sigue:Debug.Log(transform.parent.gameObject.name);

    Arrastramos tras salvar el script dentro del cubo (que est dentro de la cpsula). Play.

    Aparece el nombre de la cpsula,que es lo que pretendamos. Fijaros cmo a travs del operador punto le decimos al Unity (leyendo de derecha a izquierda) que busque el nombre del gameobject al que pertenece el transform que es el padre del transform del gameobject que hace la consulta (al que va vinculado el script) y lo imprima en pantalla.

    Vamos ahora a darle un padre nuevo a nuestro cubo. Abrimos el script y, sin borrarla, desplazamos la declaracin que habamos escrito un par de renglones hacia abajo, y encima escribimos lo siguiente:var nuevoPadre : Transform;transform.parent = nuevoPadre; Debug.Log(transform.parent.gameObject.name);//Esto ya lo tenamos escrito

    Salvamos, y seleccionamos el cubo para que en el inspector quede expuesta la variable nuevoPadre. Arrastramos hasta ella la cmara principal desde la jerarqua y le damos al play. Nuestro cubo ha cambiado de padre.

    Pensemos, llegados a este punto, que nuestro transform hijo tiene una posicin, rotacin y escala relativa con respecto a su padre. Si cambiamos su padre, stas obviamente cambiarn.

    root:

    var root : Transform

    Esta variable hace referencia al transform ms alto de la jerarqua, por lo que guarda muchas similitudes con parent, pero referida al parent de los parent del que depende nuestro transform. Si ste tiene un padre sin padre, el resultado equivaldr a parent. Si su padre tiene un padre que tiene un padre que tiene un padre, la variable ir escalando hasta llegar al ltimo padre y devolver su valor (o nos permitir modificar dicho valor). Para el caso de que el objeto que invoca esta variable no tenga padre, lo que se devuelve no es null, sino el propio transform que la invoca.

    childCount:

    var childCount : int

    Es meramente una variable de tipo int que indica el nmero de hijos que tiene un transform.

    lossyScale:

    var lossyScale : Vector3

  • Es esta una variable de tipo Vector3 que representa la escala global del objeto. Es una variable de slo lectura, por lo que obviamente no podemos modificar la escala global (la local ya vimos que s con localScale) asignando valores a esta variable.

    Y con esto acabamos las variables y nos lanzamos a por las funciones, que tambin son unas cuantas.

    13. CLASE TRANSFORM (IV)

    FUNCIONES:

    Translate:

    function Translate (translation : Vector3, relativeTo : Space = Space.Self) : void

    Esta funcin nos permite mover el transform en la direccin y distancia indicados en el parmetro de tipo Vector3 traslation. El segundo parmetro nos permite decidir si el transform se mover segn sus propias coordenadas (locales) o lo har en base al espacio global. Por defecto, si no indicamos el segundo parmetro, Unity entiende que el transform se mover segn sus propias coordenadas (Space.Self).

    Desarrollemos esto con un ejemplo:

    Para empezar, desemparentamos nuestro cubo respecto de la cpsula, arrastrndolo a un espacio vaco de la jerarqua. Acto seguido, con el cubo seleccionado, en el inspector le asignamos a la coordenada Y de rotacin un valor de 35. Abrimos el script que tenemos asignado al cubo y tecleamos: transform.Translate(Vector3.forward * 5);

    Salvamos y presionamos play. El cubo avanza cinco unidades en su eje Y, y recalcamos lo de "su" eje, ya que al no aadirle un segundo parmetro que diga lo contrario, se mueve segn sus coordenadas locales.

    Para ver la diferencia, aadamos como segundo parmetro a la funcin la variable World de la enumeracin Space, para que sustituya al parmetro por defecto Space.Self: transform.Translate(Vector3.forward * 5, Space.World);

    function Translate (x : float, y : float, z : float, relativeTo : Space = Space.Self) : void

    Existe un segundo prototipo para la funcin Translate. Como se puede comprobar, se diferencia del primero en que en lugar de pasar como parmetro para establecer la direccin y longitud del movimiento un Vector3, se pasa cada eje como un float independiente.

    function Translate (translation : Vector3, relativeTo : Transform) : voidfunction Translate (x : float, y : float, z : float, relativeTo : Transform) : void

    Estos dos prototipos varan de los anteriores en el segundo parmetro, que ya no versa sobre si el movimiento se har tomando en cuenta las coordenadas locales del objeto que se mueve o las globales de la escena. En cambio, aqu el movimiento de nuestro transform vendr fijado por otro transform. De esta manera, nos moveremos de acuerdo con las coordenadas locales de ese segundo objeto al que hacemos referencia. Si por ejemplo el parmetro relativeTo es una cmara, la derecha del traslation no es la derecha local de nuestro transform, o la derecha global, sino la derecha de la cmara. Por ejemplo:

  • Tecleamos lo siguiente en nuestro scripttransform.Translate(Vector3.right * 5);

    Esto har que nuestro cubo se mueva cinco unidades a su derecha.

    Y ahora modificamos el script para que quede as: var miCamara : Transform;transform.Translate(Vector3.right * 5, miCamara);

    Arrastramos la cmara principal hasta la variable miCamara. De nuevo al play.

    Ahora el cubo se mueve cinco unidades a la derecha de la cmara.

    Como ltimo apundo hemos de aadir que si relativeTo es nulo, las coordenadas del movimiento pasarn a ser las globales.

    Rotate:

    function Rotate (eulerAngles : Vector3, relativeTo : Space = Space.Self) : void

    Esta funcin permite rotar un transform. Acepta como parmetro un Vector3 con los grados de rotacin en ngulos Euler. Por defecto, al igual que sucediera con la funcin translate, el transform rota sobre sus coordenadas locales, pudiendo hacerlo segn las coordenadas globales si se lo indicamos con Space.World como segundo parmetro.

    Veamos un ejemplo. Rehagamos nuestro script con el siguiente contenido: function Update() {transform.Rotate(Vector3.right * 25 * Time.deltaTime);transform.Rotate(Vector3.up * 20 * Time.deltaTime, Space.World);}

    Al darle al play, observaremos que nuestro cubo rota sobre su eje X a razn de 25 grados por segundo mientras a la vez gira sobre el eje Y global a razn de 20 grados por minuto.

    Es de sealar que ambas instrucciones se encuentran contenidas dentro de la funcin Update. Esta funcin, que en su momento estudiaremos con la debida profundidad, es llamada cada frame por nuestro ordenador (el framerate de cada ordenador vara), de tal manera que el movimiento es contnuo, pues los valores de rotacin de nuestro cubo son actualizados constantemente.

    Precisamente porque el framerate de cada ordenador es distinto, y para evitar que en funcin de cada PC los objetos se movieran ms o menos deprisa (lo que tendra indeseables consecuencias, sobre todo en juegos en lnea multijugador), se suele utilizar la variable de la clase Time Time.deltaTime. Time.deltaTime lo que consigue es transformar la unidad de frecuencia de cualquier tipo de movimiento de frames a segundos. Si por ejemplo en el ltimo script no hubiramos usado estas variables de tiempo, el cubo girara 25 y 20 grados cada frame, quedando al albur de cada ordenador la frecuencia real que eso supondra. Al multiplicarlo por Time.deltaTime las rotaciones dependern del tiempo y en consecuencia se producirn con la misma cadencia en cualquier ordenador.

    function Rotate (xAngle : float, yAngle : float, zAngle : float, relativeTo : Space = Space.Self) : void

    En esta versin de la funcin, la nica diferencia es que se sustituye el Vector3 por tres floats, en los cuales se consignarn igualmente los grados de rotacin.

    function Rotate (axis : Vector3, angle : float, relativeTo : Space = Space.Self) : void

  • La variante que nos ofrece este tercer prototipo es que por un lado se indica en el primer parmetro sobre qu eje queremos que rote el transform, y en un segundo parmetro de tipo float le hemos de indicar el nmero de grados ha de rotar.

    14. CLASE TRANSFORM (V)

    RotateAround:

    function RotateAround (point : Vector3, axis : Vector3, angle : float) : void

    Esta funcin nos permite que nuestro transform rote alrededor de un punto (u objeto situado en ese punto), como si orbitara.

    El parmetro point sera el punto, descrito por un Vector3, alrededor del cual queremos hacer girar nuestro transform. Axis nos servir para indicar sobre qu eje queremos girar, y angle el nmero de grados por frame (si est dentro de la funcin update) o por segundo (si implicamos la variable de clase Time.deltaTime) que queremos que gire. Obviamente, aqu estamos variando tanto la rotacin como la posicin de nuestro transform.

    si por ejemplo quiriramos que nuestro cubo girara sobre su eje Y alrededor del centro exacto del espacio global, a razn de 20 grados por segundo, escribiramos este script: function Update() {transform.RotateAround (Vector3.zero, Vector3.up, 20 * Time.deltaTime);}

    Dado que el cubo se hallaba ya en las coordenadas globales 0,0,0 el script anterior lo nico que consigue es que el cubo parezca girar sobre s mismo. Vamos a hace lo siguiente: colocamos en el inspector a nuestro cubo en position.x = -1, y volvemos a darle al play.

    Y vemos ya ms claramente cmo orbita alrededor del punto dado, cambiando simultneamente de posicin y de rotacin.

    Podemos hacer tambin con esta funcin que un objeto orbite alrededor de otro. Escribimos: var centroDeGravedad: Transform; function Update() {transform.RotateAround (centroDeGravedad.position, Vector3.up, 20 * Time.deltaTime);}

    Acto seguido, arrastramos el objeto sobre el que queremos que orbite nuestro cubo, en este caso la cpsula. Le damos al play y comprobamos.

    Observemos que aunque arrastramos un gameobject, lo que est esperando nuestro scrips es un transform. Lo que sucede es que -tal como vimos en las primeras lecciones- todos los componentes de nuestro gameobject comparten

  • nombre, de tal manera que cuando en casos como este Unity detecta que uno de los componentes del gameobject que le estamos arrastrando tiene el nombre y el tipo del que est esperando para cumplimentar una variable, automticamente selecciona -en este caso- el transform homnimo y no el gameobject.

    Ese transform, no obstante, no es del tipo Vector3 que espera nuestra funcin. S que lo es la variable position del mismo, que adems contiene las coordenadas globales del transform sobre el que queremos orbitar.

    Podis probar a sustituir en el segundo parmetro Vector3.up por Vector3.right (o Vector3.forward), y comprobar qu es lo que sucede.

    LookAt:

    function LookAt (target : Transform, worldUp : Vector3 = Vector3.up) : void

    El nombre de esta funcin se podra traducir al castellano como "mira a", y es exactamente eso lo que hace: Rotar nuestro transform hacia un objetivo -parmetro target- que es asimismo de tipo transform (y no Vector3, como en el caso de la funcin anterior). Esa rotacin se efecta sobre un eje global, y por defecto ste ser el eje Y. Ello implicar que nuestro transform, si no le indicamos lo contrario, girar hacia la derecha y la izquierda "mirando" al transform que hayamos designado como objetivo.

    Huelga decir que esta funcin se usa mucho para cmaras, cuando pretendemos que stas sigan en todo momento a un determinado personaje.

    Es importante avisar de que para que el transform gire sobre el eje que le indiquemos, ha de estar totalmente perpendicular a dicho eje global.

    Vamos a la prctica. En el Proyecto, colocamos el ratn sobre la carpeta Mis Scripts y le damos al botn derecho=>create=>javascript. Al nuevo script lo renombramos como MiSegundoScript y le damos doble click para se se nos abra el editor.

    Desplazamos un poco hacia abajo la funcin Update, y la dejamos como sigue: var sigueme : Transform;function Update () {transform.LookAt(sigueme);}

    Salvamos, y la arrastramos hasta nuestra cmara en la jerarqua. Seleccionamos entonces la cmara y arrastramos ahora hasta la variable sigueme nuestro cubo.

    Previamente a darle al play nos aseguramos de que el otro script (MiPrimerScript) que tenemos vinculado al cubo, contenga lo siguiente:var centroDeGravedad: Transform; function Update() {transform.RotateAround (centroDeGravedad.position, Vector3.up, 20 * Time.deltaTime);}

    Y ahora s, ya podemos darle al play y perseguir a nuestro cubo.

    function LookAt (worldPosition : Vector3, worldUp : Vector3 = Vector3.up) : void

    Este segundo prototipo se diferencia del primero en que el objetivo que ha de seguir nuestro transform no es otro transform, sino un Vector3. Esto tiene sentido, siguiendo con el ejemplo de la cmara, si quisiramos que sta enfocara un punto concreto de la escena (en coordenadas globales)

    Por ejemplo, si queremos que una cmara enfoque al centro de la escena, le vinculamos este script: transform.LookAt(Vector3.zero);

  • Otra posibilidad que nos permite esta funcin es que sea nuestro jugador, desde su teclado, quien se encargue de decidir dnde ha de enfocar la cmara. Para ello tendremos que recurrir a una clase que an no hemos visto, la clase input.

    Abrimos el script MiSegundoScript, y tecleamos lo que sigue: function Update () {transform.LookAt(Vector3(Input.GetAxis("Horizontal") * 10.0,0,0));}

    Le damos al play y movemos la cmara con las flechas de desplazamiento horizontal. Quizs notemos que el resultado es un poco basto, pero meramente quera que tuvirais una idea de las utilidades de esta funcin. Tambin podramos controlar la cmara desde el ratn y no desde el teclado. Veremos todo esto en profundidad cuando estudiemos la clase input.

    Nos vemos en el siguiente captulo.

    15. CLASE TRANSFORM (VI)

    TransformDirection:

    function TransformDirection (direction : Vector3) : Vector3

    Esta funcin toma como nico parmetro la direccin local de un transform almacenada en un Vector3 y la convierte en direccin global, devolvindola en otro Vector3.

    La utilidad de esta funcin puede resultar un poco confusa al principio. Pensemos, para intentar aproximarnos al concepto, que quisiramos hacer nuestra propia versin del FIFA 2011. Modelamos un campo de futbol y, para darle ms realismo y emocin al juego, colocamos varias cmaras en los laterales del campo, cmaras que mediante LookAt iran siguiendo los lances del juego. El problema que nos encontraramos es que cuando Messi (por poner un caso) est avanzando hacia delante (en las coordenadas globales), en cambio en nuestra cmara lateral pareciera que lo est haciento -por ejemplo- hacia la izquierda. Y en consecuencia, cuando intentamos que nuestro defensa lo ataje movindolo hacia atrs (segn la perspectiva que nos da la cmara lateral) veremos consternados que el defensa en lugar de retroceder se desplaza hacia la derecha.

    Esto no nos pasara si, gracias a esta funcin, convertimos la coordenada "hacia detrs" de nuestra cmara en su equivalente en coordenadas globales. Si esa funcin se la aplicamos a todas las cmaras, no tendremos que estar pensando "Ojo, que esta cmara est en el gol norte, por lo que si me baso en ella cuando haga un movimiento, he de recordar que arriba es abajo y viceversa y la derecha es la izquierda y bla, bla, bla".

    Vemoslo en un ejemplo.

    En Unity borramos el script MiSegundoScript de la cmara. Colocamos en el inspector a nuestro cubo en las coordenadas 0.0.0. con una rotacin igualmente de 0,0,0. Asimismo, la cmara debera estar en las coordenadas de posicin 0,1,-5 con los tres ejes de rotacin a 0.

    Abrimos MiPrimerScript. Tecleamos esto: var miCamara : Transform;var estaEsMiDerecha : Vector3;estaEsMiDerecha = miCamara.TransformDirection(Vector3.right);transform.Translate(estaEsMiDerecha * 3);

    Arrastramos la cmara a miCamara. Le damos al play. El cubo se mover 10 unidades a la derecha. Pero, a la derecha de quin?. Si observamos, la derecha del cubo es tambin la derecha de la cmara.

    Para averiguarlo, vamos a recolocar la cmara en estas coordenadas: Position: -10, 1, -0.5

  • Rotation: 0, 90, 0

    Y de nuevo le damos al play. Obviamente, el cubo se mueve a la derecha de la cmara. Visto desde la ventana game, coincidir ahora "nuestra" derecha (entendiendo como tal la que nos muestra la pantalla) con el sentido el movimiento.

    El script no necesita mucha explicacin. Inicializamos una variable con el transform de la cmara que hemos arrastrado. La derecha de esa cmara la almacenamos en una variable de tipo Vector3, la cual luego pasamos como parmetro a nuestra funcin TransformDirection para que nos convierta la derecha de nuestra cmara en la derecha de las coordenadas globales. A partir de ah, todo lo que le suceda a la derecha de nuestra cmara (por as decirlo) le estar pasando a la derecha del mundo.

    function TransformDirection (x : float, y : float, z : float) : Vector3

    Es la misma funcin, pero aplicando como parmetros 3 floats para cada eje en lugar de un Vector3.

    InverseTransformDirection:

    function InverseTransformDirection (direction : Vector3) : Vector3

    o bien

    function InverseTransformDirection (x : float, y : float, z : float) : Vector3

    Se trata obviamente de la funcin inversa a la anterior, y por consiguiente transforma una direccin global en direccin local.

    Veamos un ejemplo:

    Devolvemos antes que nada a nuestra cmara a su lugar y rotacin originales: Position: 0,1,-5Rotation: 0,0,0

    La posicin y rotacin de nuestro cubo, por su parte, est totalmente a 0.

    Abrimos MiPrimerScipt, y tecleamos: var estaEsLaDerechaDelMundo : Vector3;estaEsLaDerechaDelMundo = transform.InverseTransformDirection(Vector3.right);transform.Translate(estaEsLaDerechaDelMundo * 2);

    Como vemos, declaramos una variable de tipo Vector3 que luego inicializamos de la siguiente forma: le pasamos a la funcin InverseTransformDirection el parmetro Vector3.right, que en esta funcin representa la derecha en coordenadas globales (no la derecha de ningn transform). Esa derecha del mundo, global, es "traducida" por la funcin en una coordenada local susceptible de usar por cualquier transform y es asignada,como decamos, a nuestra variable estaEsLaDerechaDelMundo. Dicha variable, por ltimo, es pasada como parmetro de movimiento al transform del gameobject que tiene vinculado el script (en este caso el cubo).

    La probamos?

    El cubo se desplaza a la derecha. Pero, para saber si la derecha es la derecha del cubo o la derecha global, podemos en el inspector darle un valor al rotation.Y del cubo de 45 grados, por ejemplo. Y probamos de nuevo.

    Definitivamente, el cubo se mueve ahora siguiendo el eje X global, y no el suyo local.

  • 16. CLASE TRANSFORM (y VII)

    TransformPoint:

    function TransformPoint (position : Vector3) : Vector3

    function TransformPoint (x : float, y : float, z : float) : Vector3

    A diferencia de TransformDirection, lo que esta funcin transforma de local en global es la posicin y no la direccin. Esto es, esta funcin no versa sobre si un transform se desplaza a su derecha o a la derecha de las coordenadas globales, sino de si las coordenadas en que se encuentra el transform son globales (respecto al mundo) o locales (respecto al padre de dicho transform). Con esto entendido, como decimos, esta funcin acepta como parmetro las coordenadas locales de un transform (su ubicacin respecto de la de su padre) y devuelve dichas coordenadas traducidas a coordenadas globales.

    Lo veremos ms claron con un ejemplo. Con el cubo en position 0,0,0 y rotation en 0,0,0 (si no,no funcionar) arrastramos el cubo dentro de la cpsula en la Jerarqua, para convertir a cubo en hijo de la cpsula. Si seleccionamos el cubo, vemos en el inspector que sus coordenadas de posicin han pasado a ser locales respecto de su padre. Abrimos MiPrimerScript (que debera seguir siendo parte del cubo) y escribimos el siguiente script: var coordenadasLocales : Vector3;var coordenadasGlobales: Vector3;var coordenadasTransformadas: Vector3;

    coordenadasLocales = transform.localPosition;coordenadasGlobales = transform.position;coordenadasTransformadas = transform.position = transform.TransformPoint(transform.localPosition);

    Debug.Log("El cubo tiene las coordenadas locales " + coordenadasLocales.ToString() + " las globales " + coordenadasGlobales.ToString() + " y las transformadas " +coordenadasTransformadas.ToString());

    El ejemplo parece ms complicado de lo que es. Declaramos tres variables de tipo Vector3 para que contengan las tres coordenadas que imprimiremos para nuestro cubo (los nombres de las variables son lo suficientemente explicativos). Acto seguido inicializamos las dos primeras variables con la posicin global y local del transform, y la tercera con las coordenadas que tendr el transform cuando convirtamos sus coordinadas locales (respecto de la cpsula) en globales.

    Salvamos y le damos al play. Observamos que por un lado el cubo se desplaza en direccin y distancia opuesta al cilindro, ya que el -3,-4,-2 que constituan sus coordenadas locales ahora ha pasado a ser su posicin en coordenadas globales.

    InverseTransformPoint:

    function InverseTransformPoint (position : Vector3) : Vector3

    function InverseTransformPoint (x : float, y : float, z : float) : Vector3

    Funcin inversa a la precedente, que por tanto transforma la posicin de un transform dado del espacio global al espacio local.

  • DetachChildren:

    function DetachChildren () : void

    Es una funcin muy sencilla que sirve para desparentar los hijos. Para probarla debemos eliminar el script vinculado al cubo, y posteriormente hacer doble click en MiPrimerScript en el Proyecto para teclear lo siguiente:transform.DetachChildren();

    Salvamos y lo arrastramos a la cpsula, que es el transform que tiene hijos. Le damos al play y observaremos cmo en la Jerarqua el cubo deja de aparecer como hijo de la cpsula.

    Esta funcin es til, entre otras cosas, cuando por alguna razn queramos destruir al objeto padre sin destruir a sus hijos:transform.DetachChildren();Destroy(gameObject);

    Find:

    function Find (name : String) : Transform

    Con esta funcin podemos buscar por su nombre -y en su caso acceder- a un transform hijo de nuestro transform. La funcin devuelve dicho transform hijo, si lo encuentra. Si no lo encuentra, retorna null.

    Si tenemos que buscar a varios niveles, esto es, hijos de los hijos de nuestros hijos, podemos utilizar un slash o barra inclinada (/) para recrear la jerarqua donde queremos buscar (p ej. "Cuerpo/Brazo/Mano/Indice")

    Dado que deberamos tener el cubo an dentro de la cpsula, y MiPrimerScript vinculado a sta, lo aprovecharemos para realizar un ejemplo: var aquiMiHijo : Transform;

    function Update() {aquiMiHijo = transform.Find("Cubo");aquiMiHijo.Rotate(Time.deltaTime*60, 0, 0);}

    Como podemos comprobar al darle al play, la funcin Find encuentra un transform hijo llamado "Cubo" (recordemos que, por un lado hemos de suministrarle un string, esto es, no nos debemos olvidar de las comillas, y por otro lado que todos los componentes de un gameobject comparten por defecto el mismo nombre que ste, as que el transform del Cubo se llama Cubo), y almacena ese transform que encuentra dentro de la variable aquiMiHijo. A partir de ah, podemos utilizar esa variable como si fuera un alias del propio transform.

    IsChildOf:

    function IsChildOf (parent : Transform) : boolean

    Esta funcin casi no requiere explicacin. Devuelve true si el transform que hace la pregunta es hijo, "nieto" o incluso idntico al transform parent que pasamos como parmetro. En otro caso, devuelve false.

    Y con esto acabamos la clase transform, que como deca al inicio es una de las diez clases ms importantes de la API de Unity.

  • 17. CLASE RIGIDBODY (I)

    Nos disponemos a estudiar otra de las clases vitales para entender el funcionamiento de Unity. Es raro en juego en el que no interviene de alguna manera la fsica (un enemigo se cae, un personaje salta,