Introducción

En este artículo vamos a ver qué es un script en programación, vamos a escribir un script simple en el bloc de notas que haga una determinada tarea y dar varios ejemplos de aplicación.

Qué es un Script en programación

En términos simples un script en programación es un documento de texto donde colocamos instrucciones u órdenes que luego serán ejecutadas por un dispositivo inteligente. Estas instrucciones estarán escritas en algún lenguaje de programación en el que se debe respetar su sintaxis para que cada instrucción pueda ser traducida a lenguaje de máquina. Además cada script será un archivo con un formato que dependerá del lenguaje en el que esté escrito.

El conjunto de todos los scripts de nuestro programa junto con otros elementos conforman el código fuente.

Un vídeo en el que creamos un script en Unity para resolver un problema

En el siguiente vídeo puedes ver el procedimiento que aplico al crear un Script, primero analizamos el problema, planteamos un estrategia para resolverlo y finalmente creamos el script con las instrucciones necesarias.
El problema consiste en determinar si un número dado es par o impar, observa como antes de empezar a escribir código lo que se hace es crear una estrategia para resolver el problema, poner sobre la mesa algún fundamente teórico o datos que nos ayuden a resolver el problema, yo diría que esta es la parte más importante a la hora de programar, más importante que escribir el código en los scripts. Una vez que planteamos la estrategia procedemos a traducirla en instrucciones de código.


Escribamos un Script en este mismo momento!

El campo de aplicación de la programación es muy amplio, podemos escribir programas para un sinnúmero de propósitos. Te invito a realizar el siguiente experimento para la plataforma Windows:

Vamos a abrir la aplicación Bloc de Notas o Notepad, el editor de texto de Windows de toda la vida y ecribamos las siguientes dos líneas:

timeout /t 6000 /nobreak

shutdown -h

Debería verse de la siguiente manera:

Fig. 1: Script hecho en Bloc de Notas. Al ejecutarse, la computadora espera 6000 segundos y luego entra en estado de hibernación.

Hacemos clic en “file-save file as” o “archivo-guardar cómo” y nos aseguramos de elegir la opción “All Files (*.*)” o “Todos los archivos (*.*)“, como se observa en la figura 2. Le damos un nombre y lo terminamos con la extensión .bat” que es la extensión Batch o lotes. Guardémoslo en el escritorio para encontrarlo rápidamente.

Fig. 2: Ventana de Guardar Cómo del Bloc de Notas. Ponerle la extensión “.bat”.

Archivo ejecutable con instrucciones de código

CUIDADO! SI EJECUTAS ESTE ARCHIVO TU COMPUTADORA SE APAGARÁ AUTOMÁTICAMENTE CUANDO PASEN 100 MINUTOS

Luego de seguir los pasos anteriores generamos un archivo Batch que podemos ejecutar haciendo doble clic, como se puede ver a continuación.

Fig. 3: Archivo que se genera al guardar con la extensión.

Al ejecutarlo se nos abre el terminal de windows indicando que se encuentra esperando una determina cantidad de tiempo que hemos indicado en nuestro Script.

Fig. 4: Ejecución del Script “hibernar.bat”.

Cuando se cumplan los 6000 segundos que indicamos en el Script, se ejecutará la segunda instrucción: “shutdown /h” que pondrá nuestro equipo en estado de hibernación.

Así de fácil hemos escrito un Script para que cumpla con una determinada función.

Antes tenía muchos problemas de sueño y la única forma de dormirme era escuchando algún vídeo con un volumen muy bajo, este Script que acabamos de hacer lo utilicé mucho para que la computadora no quede encendida toda la noche.

Otros ejemplos de Scripts

A continuación voy a mostrar una serie de ejemplos de Scripts que nos podemos encontrar al empezar un proyecto que involucre programación.

Juegos en Unity

Si queremos desarrollar un juego en Unity, nos vamos a encontrar con los Scripts en lenguaje C#. Es posible escribir estos Scripts en Bloc de Notas siempre que los guardemos con la extensión “.cs”, pero eso sería innecesariamente complicado. Contamos con Editores que nos permiten comprobar la sintaxis, auto completar y nos hacen el trabajo más fácil.

Fig. 5: Script en c# perteneciente al desarrollo de la serie “Fundamentos de Unity”.

Aplicaciones Android

Quizás estás interesado en hacer una App para Android, en ese caso una opción es utilizar el software Android Studio.

En Android Studio se escriben distintos scripts, los Scripts en lenguaje XML se utilizan para definir el diseño de nuestra aplicación y los script en lenguaje Java se utilizan para la lógica de nuestra aplicación, figuras 6 y 7 respectivamente.

Fig. 6: Script XML para el diseño de una aplicación en Android Studio.

Fig. 7: Script Java para el diseño de una aplicación en Android Studio.

Cuando estudiaba programación en la universidad usé entornos de desarrollo como Eclipse o NetBeans, en los que he escrito Scripts en Java que luego pude simular en la consola y poder analizar los resultados. Aquí dejo un ejemplo de un Script en Java utilizando el IDE NetBeans:

Entornos de programación (IDE)

Fig. 8: Script Java utilizando el IDE NetBeans. El Script es para el estudio de la programación concurrente.

Proyectos Arduino

Quizás estamos interesados en la electrónica y queremos programar un Arduino. En ese caso vamos a escribir Scripts en formato “.ino” utilizando el IDE de Arduino.

Fig. 9: Script por defecto de Arduino.

Otros ejemplos pueden ser Scripts para MatLab o SciLab, plugins para Blender, Scripts para tratar los datos en tablas de Excel, scripts para ejecutar macros y muchos otros ejemplos.

Conclusión

Los Scripts en programación son conjuntos de instrucciones escritas en algún lenguaje y que posteriormente serán ejecutadas por un dispositivo inteligente, sea un ordenador, un móvil, etcétera.

El campo de aplicación de la programación es muy extenso. Dependiendo de lo que queramos hacer vamos a tener distintas herramientas para escribir Scripts y estos estarán en distintos formatos.

Introducción

En este artículo vamos a estudiar cómo utilizar el método OnTriggerStay para detectar al personaje en una determinada región y aplicar acciones cuando eso ocurra. Concretamente vamos a hacer que las estaciones de daño y regeneración de salud afecten al personaje.

Página principal del proyecto

Antes de empezar te invito a ver el video que hice para resumir este artículo.

Procedimiento

Vamos a utilizar la estación “OnTriggerStay“, la cual consiste en dos aparatos con forma de detector de metal, uno rojo y otro verde y cuya función es afectar nuestro sistema de salud. Al entrar en la estación roja nuestra salud debe disminuir y con la estación verde debe aumentar.

Fig. 1: Estación OnTriggerStay del GameDevLab.

Todos estos objetos se encuentran dentro del empty GameObject#5 OnTriggerStay“. Tenemos una estación de regeneración (HealthStation) y otra de daño (DamageStation).

Fig. 2: Jerarquía de la escena. Todos los elementos de la estación están en el GameObject #5 OnTriggerStay.

Tanto HealthStation como DamageStation tienen asignado el mismo script, “OnTriggerStayAction“.

Fig. 3: Script asignado a los GameObjects “HealthStation” y “DamageStation”.

En el inspector podemos modificar la cantidad de salud que nos dá o nos quita a través del float “amountHealth” y podemos elegir el tipo de estación que es.

Fig. 4: El script nos permite elegir si la estación hace daño o regenera.

Para que las estaciones puedan hacer su trabajo es necesario que el jugador tenga algún tipo de sistema de salud al cual afectar. Esto ya viene resuelto en el GameDevLab. El GameObject FirstPersonPlayer tiene asignado un script llamado “HealthSimple” que cuenta con las variables y métodos necesarios para proporcionarle al jugador un sistema de salud simple.

Fig. 5: Script asignado al GameObject FirstPersonPlayer.

Resolución

Al abrir el script OnTriggerStayAction por primera vez nos encontramos con lo que se ve en la figura 6. Hay una región definida entre comentarios donde se sugiere redefinir el método OnTriggerStay.

Fig. 6: Script OnTriggerStayAction sin completar.

¿Cómo funciona el método OnTriggerStay?

Este método está definido en la clase MonoBehaviour, por lo tanto estará presente por defecto en cualquier script nuevo que hagamos.

Se ejecuta automáticamente si un GameObject con Collider entra en la región definida por otro Collider que está en Modo Trigger. Además al menos uno de los dos GameObjects debe tener un componente RigidBody asignado.

Fig. 7: Mientras los colliders no se toquen, el método OnTriggerStay no se ejecuta.

Fig. 8: Los colliders se están tocando, por lo tanto el método OnTriggerStay se ejecuta.

En la figura 9 se muestra la resolución del ejercicio.

Fig. 9: Resolución del script OnTriggerStayAction.

Redefinimos el método OnTriggerStay que recibe como parámetro el Collider que ha entrado en contacto con el trigger. Le damos el nombre “col” a la referencia de este Collider.

Lo que tenemos que hacer es en primer lugar determinar si el GameObject cuyo Collider entró en contacto tiene un sistema de salud al cual afectar, esto lo hacemos obteniendo la referencia de la componente HealthSimple que puede estar asignada a este GameObject.

Si el GameObject tiene sistema de salud, tendremos una componente HealthSimple, en caso contrario nuestra variable va a tener el valor null. Usamos esto como condición en la sentencia if.

Si la condición es verdadera ejecutamos el método playerIsUnderTheStation y pasamos como parámetro la referencia de HealthSimple que se encuentra en health.

Con esto ya tenemos resuelto el problema del video, a partir de ese punto la componente HealthSimple se encargará de manejar el tema de la salud.

Fig. 10: Valor de salud luego de unos segundos en la estación de daño.

Al entrar en la estación de daño nuestra salud comienza a mermar, si llega al valor cero la escena se reinicia.

Al entrar en la estación de regeneración, la salud comienza a aumentar hasta llegar al valor máximo.

Fig. 11: Valor de salud luego de unos segundos en la estación de regeneración.

Conclusión

El objetivo de este artículo y video correspondiente es estudiar una aplicación del método OnTriggerStay, para entender cómo funciona y posteriormente poder aplicarlo a cualquier situación en la que pueda ser útil.

Existen muchas situaciones en las que este método podría servir, por ejemplo modelar el comportamiento de una placa de presión, hacer que nos afecte una fuerza opuesta a la gravedad que nos haga levitar si estamos en determinada región. En definitiva este método es una herramienta útil que tenemos que entender.

No es el punto de este artículo estudiar el sistema de salud, pero es interesante observar que las estaciones van a actuar sobre cualquier GameObject que cuente con el script HealthSimple, independientemente de que se trate del jugador. Esto nos sugiere que en nuestro juego podríamos tener un solo script que administre la salud de cualquier ser viviente de nuestro juego y tenga propiedades comunes para todos, así como propiedades únicas para cada ser. Crear este tipo de soluciones es lo que me resulta más interesante

Introducción

En este artículo vamos a estudiar cómo crear y destruir GameObjects en tiempo de ejecución. Lograr esto es muy importante para crear juegos, porque nos permite colocar objetos cuando y donde queramos y poder posteriormente destruirlos.




Procedimiento

En este desafío vamos a usar la estación Creation/Destruction que se ve en la figura 1, para hacer que una pequeño humanoide aparezca y desaparezca de manera cíclica mientras el juego se desarrolle.

Página principal del proyecto

Fig. 1: Estación Creation Destruction del GameDevLab.

Fig. 2: Estación Creation Destruction del GameDevLab. Lucy aparece por unos segundos.
Fig. 3: Estación Creation Destruction del GameDevLab. Lucy desaparece.

En las figuras 2 y 3 se ilustra el ciclo de aparición y desaparición del personaje.

Fig. 4: Campos del Script CreationDestruction correspondiente al desafío del video 4 de la Serie Fundamental.

Las figuras 1 y 2 muestras los campos y componente en el inspector del Script “CreationDestruction”.

Fig. 5: El GameObject “#4 Creation Destruction” tiene asignado el Script Creation Destruction. Esto se ve en el inspector.

El desafío consiste en completar los métodos createObject() y destroyObject() del Script.



Resolución

Creación del objeto

Para la creación del objeto vamos a utilizar el método Instantiate, este método está definido en la clase MonoBehaviour que por defecto es la super clase o clase padre de todo nuevo Script que hacemos en Unity.

En este momento no tengo suficientes artículos ni videos sobre programación orientada a objetos para aclarar lo de las super clases, sé que eventualmente los haré, pero por ahora si quieres saber más investiga el tema Herencia en programación orientada a objetos.

El método Instantiate tiene más de diez variantes, pero en todas básicamente necesitamos dos cosas: el objeto que vamos a crear y en qué punto del espacio lo vamos a poner.

Haz clic aquí para leer un artículo sobre métodos en programación.

En este caso el objeto que vamos a crear lo indicamos utilizando el prefab SmallLucy que se encuentra en la carpeta Internal Use, este prefab lo colocamos en el espacio Object To Create en el inspector, como se observa en la figura 5.

Para indicar el punto del espacio donde vamos a colocar el objeto nuevo vamos a usar un GameObject llamado Position que está en la jerarquía. También lo asignamos en el inspector (figura 5).

Vamos a utilizar una variante del método Instantiate que creará el objeto y lo colocará en la jerarquía como hijo del GameObject que le indiquemos. Esto quiere decir que nuestro objeto aparecerá en la posición del GameObject padre (Position) más el desplazamiento propio que SmallLucy tenga (en este caso SmallLucy se encuentra en el origen así que su desplazamiento es cero).

Sé que el párrafo de arriba está algo confuso, más adelante me gustaría hacer un video y artículo sobre coordenadas globales y locales para poder explicar esto con más claridad. Por ahora sigamos adelante. Escribimos la siguiente línea en el método createObject:

createdObject = Instantiate (objectToCreate,position.transform);

Vamos por parte, en primer lugar createdObject es un objeto de tipo GameObject auxiliar (ver figura 4) que definimos para guardar la referencia del objeto nuevo que vamos a crear. Esto es muy importante porque sino más adelante no sabremos cuál objeto tenemos que destruir.

Haz clic aquí para leer el artículo sobre encontrar las referencias de los GameObjects en la jerarquía, también hay un video.

El método Instantiate va a devolver un objeto tipo GameObject (el objeto nuevo) y con el signo igual lo asignamos a createdObject.

Entre paréntesis indicamos los dos parámetros separamos por una coma. El primer parámetro es el objeto a crear y el segundo es la componente Transform del GameObject Position

Fig. 6: Métodos definidos en el Script CreationDestruction correspondiente al desafío del video 4 de la Serie Fundamental.



Destrucción del objeto

Para destruir al objeto usamos el método Destroy (que está definido en la clase MonoBehaviour) y le damos como parámetro la referencia del objeto que hemos guardado en el campo createdObject. Esto lo escribimos en el método destroyObject.

Comportamiento cíclico

Queremos que Lucy aparezca y desaparezca continuamente en la estación Creation Destruction, así que tenemos que hacer que ejecutar el método createObject. Luego esperar unos segundos y llamar al método destroyObject, luego esperar y volver a llamar al método crear.

Esto lo logramos primero llamando al método createObject desde Start, de esa forma al iniciar el juego, Lucy aparece. Luego al final del método crear usamos Invoke para llamar al método destroyObject luego de dos segundos. Hacemos lo mismo al final del método destroyObject, usamos Invoke para llamar al método crear. Esto lo podemos ver en la figura 6.



Conclusión

Hemos conseguido hacer aparecer nuevos objetos en el escenario en tiempo de ejecución, es decir mientras el juego está corriendo, esto no es poca cosa, entendiendo esto podemos lograr muchas cosas.

Para crear un nuevo objeto en el mundo básicamente necesitamos una copia del objeto a crear, esto lo logramos con un prefab. Y tenemos que saber exactamente dónde colocarlo, esto se puede lograr de muchas formas, por eso el método Instantiate tiene tantas variantes.

Si queremos destruir un GameObject de la jerarquía necesitamos saber cuál es, por eso es muy importante la referencia del objeto, si creamos el objeto sin asignarlo a ningún campo, el objeto estará en la jerarquía pero no podremos hacer nada con él a menos que lo encontremos nuevamente.

Introducción

En este artículo vamos a analizar cómo aplicar en Unity el método LookAt, para hacer que un GameObject mire en la dirección de otro. Esto se traduce en la orientación de uno de los ejes del GameObject de modo que apunte hacie el origen del GameObject objetivo.

Página principal del proyecto

Antes de empezar te invito a ver el siguiente video en el que se resuelve este problema:

En el siguiente vídeo explico el funcionamiento del método LookAt en Unity




Procedimiento

Vamos a trabajar con la estación Look At, la cual consiste en una cápsula de contención en la que se encuentra flotando el modelo de Lucy. El objetivo es lograr que Lucy nos siga con la mirada por todo el escenario.

Fig. 1: Cápsula de contención anti-maldad, de la estación LookAt.

En la jerarquía tenemos un GameObject llamado “#3 LookAtObject”.

Fig. 2: Jerarquía de la escena. La estación LookAt es el GameObject #3.

Este GameObject tiene asignado el Script “LookAtObject”, que se encargará de hacer que Lucy siga con su mirada al jugador.

Fig. 3: Script para completar en este desafío.

En la figura 4 se observa que hay 3 campos para completar, el primero es para el objeto observador, el segundo para el objetivo de observación y el tercero es para que Lucy mire a una altura fija del escenario, de esta forma no se sale de la frontera de su cápsula de contención.

Fig. 4: Script asignado al GameObject “#3 LookAtObject” que se ve en la figura 2.

Vamos a asignar a Lucy directamente desde la jerarquía y al personaje lo vamos a encontrar a través de su tag.

Si querés aprender distintas formas de encontrar las referencias de los GameObjects de la jerarquía para usar en los Scripts podés leer el artículo anterior o mirar este video.

Si abrimos el Script encontramos algunas cosas hechas. En el método FixedUpdate, que se ejecuta una vez cada cierto tiempo, hacemos la llamada al método “lookAtObject”, que se encargará de cumplir la función.

Fig. 5: Script LookAtObject sin completar.



En el método start que se ve en la figura 5, tenemos una instrucción que estaba comentada al momento de hacer la captura. Hay que quitar las barras de comentarios para encontrar la referencia del jugador y lograr que Lucy lo mire.

Fig. 6: Campos definidos en el Script.

En la figura 6 se observan los campos definidos en el Script. Vamos a utilizar el Vector3 llamado “objectToLookPosition” para las coordenadas a la que Lucy debe mirar.

Fig. 7: Con estas dos instrucciones logramos que Lucy mire al origen del personaje.

Dentro del método “lookAtObject” escribimos estas instrucciones:

objectToLookPosition = objectToLook.transform.position;

objectThatLooks.transform.LookAt(objectToLookPosition);

Con estas dos instrucciones logramos que Lucy esté orientada todo el tiempo hacia el origen del jugador.

Fig. 8: Lucy sigue al personaje con la mirada.



Fig. 9: Al acercarse a la cápsula, Lucy se inclina tanto que se sale de las fronteras de la cápsula.

El desafío está parcialmente resuelto, aún tenemos que solucionar el problema que se ilustra en la figura 9.

Fig. 10: Tres GameObjects mostrando sus orígenes. El origen se representa por un gizmo que muestra los ejes locales.

Primero entendamos qué está pasando.

Todos los GameObjects tienen una coordenada en el espacio donde se encuentra su origen.

El método lookAt va a modificar la orientación del objeto al que se lo aplique (en este caso Lucy). Lo hará de tal forma que el eje local forward apunte hacia el origen del objeto a mirar. Como se ilustra en la siguiente figura.

Fig. 11: Lucy con el método lookAt aplicado, personaje lejos.

Si el personaje está lejos de Lucy su inclinación es pequeña, pero al acercarse pasa lo siguiente:

Fig. 11: Lucy con el método lookAt aplicado, personaje cerca.

¿Cómo podemos corregir esto?

Tenemos que hacer que Lucy mire hacia una coordenada que esté a la misma altura que su origen. De esa forma su ángulo de inclinación será pequeño.

Ásí que vamos a sobreescribir la componente y del Vector3 por el valor de la variable “yPos” que ajustaremos desde el inspector.

Idealmente podríamos hacer que la componente y del Vector3 valga exactamente la altaltura del origen de Lucy, esto es algo que me di cuenta luego en la post producción del video. Sin embargo está bueno como ejemplo de que no hay una sola forma de resolver los problemas.

Fig. 12: Se añade una instrucción entre medio que sobreescribe el valor de la componente y del Vector3.

Se puede probar escribir la variable yPos en tiempo de ejecución, pero al parar la simulación todos los valores volverán a su valor inicial.

El valor 0.75 funciona bien para este caso. Un número menor a este hace que Lucy se incline hacia abajo y salga por el frente de la cápsula.

Fig. 13: El valor -0.3 hace que Lucy salga por el frente de la cápsula.



Fig. 14: Con yPos igual a 0.75 Lucy mira correctamente al personaje.

Fig. 15: Con yPos igual a 0.75 Lucy mira correctamente al personaje.

En el siguiente artículo estudiaremos cómo crear y destruir objetos en tiempo de ejecución. Suscríbete al canal de YouTube para estar al tanto de los nuevos videos y artículos.

ACTUALIZACIÓN IMPORTANTE

Este artículo pertenece a una serie de vídeos antiguos, actualmente hay disponible una NUEVA SERIE que aborda con mayor profundidad el tema de encontrar cualquier GameObject o Componentes de la escena desde un Script.


Introducción

En este artículo vamos a estudiar distintas formas de encontrar, desde un Script, los GameObjects (objetos) que se encuentran en la jerarquía. Entre las formas tenemos asignar directamente los objetos desde el inspector o encontrarlos a través de métodos como “FindGameObject”.

Página principal del proyecto

Antes de empezar te invito a ver el siguiente video en el que se resuelve este problema:

🟢 INTRODUCCIÓN a la serie sobre encontrar las referencias de los GameObjects y componentes de la escena en Unity

Procedimiento

Vamos a trabajar con la estación Find GameObjects, que consiste en 5 objetos sobre una mesa. La función de esta estación es regresar al estado de reposo a los objetos que son perturbados.

El problema es que inicialmente no tenemos las referencias de los objetos, así que la función no se puede llevar a cabo.

Fig. 1: Estación para encontrar objetos en estado de reposo.
Fig. 2: Estación para encontrar objetos perturbada. Luego de unos segundos los objetos desaparecen.

Fig. 3: Estación para encontrar objetos volviendo al estado de reposo. Luego de unos segundos los objetos reaparecen en su posición inicial.

Dentro del Script “FindGameObject” tenemos algo de código ya escrito.

Fig. 4: Script FindGameObject. Definición de los objetos a encontrar.

Los 5 objetos están declarados como privados y están serializados, por eso aparecen en el inspector (como se observa en la siguiente figura).

Fig. 5: Estación para encontrar objetos en estado de reposo.

En el método Start se hace la llamada a 5 métodos que se encargarán de encontrar las referencias.

Fig. 6: Llamada a los métodos que se encargan de encontrar las referencias de los objetos.

Los métodos se encuentran incompletos. Nuestro objetivo es completarlos y descubrir distintas formas de encontrar las referencias.

Fig. 7: Métodos que se encargan de encontrar las referencias de los objetos.

Forma #1: Asignar los objetos directamente en el inspector a un campo serializado.

Como se observa en la figura 4, todos los objetos están definidos como privados y están serializados, pero esto es más que nada para que veamos en el inspector, cómo al iniciar el juego encontramos la referencia.

Para la forma 1, tomamos el objeto de la jerarquía y lo arrastramos al campo en el inspector. Como se ilustra a continuación.

Fig. 8: Asignación de GameObject a campo en la jerarquía.

Con esto ya le decimos a nuestro Script qué objeto de la jerarquía es el GameObject definido con el nombre “Object1a”.

Forma #2: Asignar los objetos directamente en el inspector a un campo público.

Esta forma es prácticamente igual que la anterior, sólo que ahora el GameObject lo definimos como público.

Fig. 9: Definición de un GameObject con visibilidad pública.

Definir un campo como público hace que sea accesible desde cualquier contexto y por lo tanto también aparece en el inspector.

Como en la forma anterior, tomamos el GameObject de la jerarquía y lo arrastramos al campo en el inspector.

Forma #3: Etiquetas. Método FindGameObjectWithTag de la clase GameObject.

La clase GameObject cuenta con un método que permite encontrar un GameObject en la jerarquía utilizando las etiquetas.

Fig. 10: Menú desplegable Tag presente en todo GameObject de la jerarquía.
Fig. 11: Ventana que aparece al hacer clic en “Add Tag” (figura 10) y luego en el signo “+”.

Luego procedemos a escribir el código necesario para encontrar el GameObject desde el Script. Vamos a escribirlo dentro del método “findGameObject2” que se ve en la figura 7.

La línea de código es la siguiente:

object2=GameObject.FindGameObjectWithTag(“Object2”);

Vamos a analizarla por partes, al escribir “object2=” estamos diciendo que el campo object2 va a valer lo que sea que coloquemos luego del signo “=”. Como se observa en la figura 9, object2 es de tipo GameObject, por lo tanto lo que coloquemos luego del signo “=” tendrá que devolver un objeto de tipo GameObject.

Al escribir “GameObject.” (GameObject punto), estamos indicando que queremos acceder a los atributos y métodos que están definidos en la clase GameObject.

Ejecutamos un método que se llama FindGameObjectWithTag” (traducido significa: “encontrar GameObject con etiqueta”), este método necesita que le ingresemos (dentro de los paréntesis) el String con la etiqueta.

Leer esto e interpretarlo es algo complicado, pero de a poco va cobrando sentido conforme vamos aprendiendo.

En el video observamos que al ejecutar el juego, logramos encontrar la referencia de object2.

En el lenguaje C# se distinguen mayúsculas y minúsculas, por lo tanto hay que respetarlas.

Forma #4: Nombre. Método Find de la clase GameObject.

Como se observa en la figura 10 arriba del menú desplegable Tag, hay un espacio con el texto “Object2”, esto es el nombre que tiene asignado el GameObject y con ese nombre aparece listado en la jerarquía.

Utilizando el método Find de la clase GameObject podemos encontrar un GameObject por su nombre, de la siguiente forma:

object3=GameObject.Find(“Object3”);

Con esta instrucción, Unity revisará la jerarquía, encontrará el objeto llamado “Object3” (si es que hay uno) y lo asignará al campo object3.

Si no hay ningún GameObject llamado “Object3” en la jerarquía, el campo object3 tendrá el valor null. Si hay más de un objeto que tiene el mismo nombre, no podemos estar seguros de que vamos a encontrar el que queremos.

Forma #5: Componente única. Método FindObjectOfType<T>.

La última forma que vamos a ver es un poco más complicada.

La clase GameObject nos permite encontrar la referencia a una componente que esté presente en la jerarquía, por ejemplo la componente Transform, Collider, RigidBody, etc.

Por supuesto, prácticamente todos los GameObjects tienen la componente Transform, entonces en principio no sería muy útil. Pero ¿Qué pasaría si la componente que nos interesa es única en la jerarquía? Es decir, sabemos que hay un solo GameObject en la jerarquía que la tiene. Esto podría servirnos, porque encontrar esa componente única es equivalente a encontrar ese GameObject.

Podría ser cualquier componente siempre que sea única, en este caso creamos un nuevo Script con el nombre: “Object4Script” y se la asignamos al GameObject de la jerarquía llamado “Object4”.

Luego escribimos el siguiente código en el método “findGameObject4” (ver figura 7).

object4=GameObject.FindObjectOfType<Object4Script>().transform.gameObject;

Primero encontramos la referencia del objeto tipo Object4Script con el método FindObjectOfType<T> de la clase GameObject, luego accedemos a su componente transform por medio del operador punto y luego a la componente gameObject. El resultado de esto lo asignamos a object4.

De esta forma logramos encontrar el GameObject a partir de una de sus componentes. Por supuesto, si hay otro GameObject que también tiene ese componente, podríamos encontrar el GameObject equivocado.


Introducción

En este artículo vamos a leer entradas del teclado y el mouse en Unity utilizando la clase Input de Unity. Esta clase también nos permite acceder a otros periféricos como joysticks, entradas táctiles, acelerómetro, giróscopo, entre otras señales de entrada.

Vídeo en el que se resuelve este problema


Página principal del proyecto

Procedimiento

En el primer video vamos a trabajar en la estación de entradas, que consiste en modelos para las teclas “WASD” y botones del mouse.

El objetivo es lograr que cambien de color cuando la entrada correspondiente esté pulsada.

Fig. 1: Estación de entradas del GameDevLab.

En la jerarquía hay un GameObject llamado #1 Input. Este objeto tiene todos los elementos relacionados a la estación de entradas.

Fig. 2: Jerarquía. La estación de entradas es el GameObject llamado #1 Input.

Este GameObject tiene asignado el script Read Input, que será el encargado de leer las entradas y realizar las acciones sobre los elementos de la estación.

Fig. 3: Script Read Input asignado al GameObject #1 Input.

No es necesario hacer nada en la jerarquía. Vamos a la carpeta “Scripts to Complete”, “Video 1 – Read Inputs” y abrimos el script “ReadInput”.

Fig. 4: Script Read Input sin completar. El código faltante va en el método Update.

¿Por qué el código debe ir en el método Update?

Todos los Scripts que extienden su comportamiento de la clase MonoBehaviour tienen implícito el método Update, el cual se evalúa automáticamente en cada frame de nuestro juego.

Como se explica en el video, si nuestro juego funciona a 60 fps constantes, el método Update se ejecuta 60 veces por segundo.

La lectura de las entradas es un evento aleatorio, por lo tanto no podemos determinar en qué momento el jugador presionará un botón. Por esta razón, cuando necesitamos leer las entradas, debemos hacerlo en todos los frames.

¿Cómo hacemos la lectura de las entradas?

Imaginemos que podemos preguntar a la computadora en lenguaje coloquial por el estado de las entradas, ¿Qué preguntas le haríamos?

Preguntas simples como las siguientes.

Fig. 5: Preguntas que debemos hacer.

Si la respuesta a estas preguntas es si, ejecutaremos una acción apropiada para cada una.

Ya tenemos la idea, ahora solo tenemos que hacer estas preguntas utilizando un lenguaje que la computadora entienda.

Utilizando la sentencia if, podemos hacer preguntas cuya respuesta es verdadero o falso.

La clase Input de Unity se encarga de manejar las entradas, así que recurrimos a sus métodos para resolver el problema. Tenemos el método GetKey para las teclas y GetMouseButton para los botones del mouse, en el argumento de estos métodos indicamos la tecla utilizando el enum KeyCode y el botón del mouse usando enteros, como se observa en la figura 6.

Solución

Fig. 6: El código faltante está completo.

Con esto logramos que la estación de entradas cumpla su función.

Fig. 7: Estación de entradas. Tecla W pulsada. 
Fig. 8: Estación de entradas. Tecla A pulsada. 
Fig. 9: Estación de entradas. Clic izquierdo. 
Fig. 10: Estación de entradas. Clic derecho. 

Conclusión

Como primera conclusión me gustaría mencionar cómo pasamos de pensar el problema en lenguaje coloquial (figura 5), para luego escribirlo en lenguaje C# (figura 6). Pensar los problemas de esta forma resulta más simple, porque nos abstraemos del lenguaje de programación.
Un paso intermedio entre este lenguaje coloquial y el código C# sería el pseudocódigo.

Leer entradas es la forma que tenemos de saber qué quiere hacer el jugador, así que tenemos que definir qué entradas vamos a ofrecer y cuándo queremos leerlas (por ejemplo quizás no nos interesa leer las entradas WASD cuando mostramos una cinemática).

Los eventos de entrada son sucesos aleatorios, es por eso que debemos leerlos de manera continua utilizando el método Update.

Para evitar complicaciones es recomendable hacer la lectura de las entradas en un solo Script.

Puede haber otro tipo de entradas, como un JoyStick o entrada táctil. Debemos estudiar la clase Input de Unity para entender cómo manejarlas.

Introducción – ¿Qué es un método en programación?

ACLARACIÓN IMPORTANTE ANTES DE EMPEZAR
En programación, cuando vean la palabra “MÉTODO” normalmente se trata de un CONJUNTO DE INSTRUCCIONES DE CÓDIGO agrupadas dentro de una función. Recuerdo que este término me resultaba un poco extraño y solo cuando estudié programación orientada a objetos entendí la interpretación de la palabra “Método” en este contexto.

En este artículo vemos qué son los métodos en programación y encontrarás un vídeo de mi canal de YouTube en el que está resumida esta informacion.

Los métodos de programación sirven para agrupar instrucciones de código y luego este conjunto de instrucciones pueden ser llamadas cuantas veces sean necesarias simplemente haciendo la “llamada al método“, esto nos permite reutilizar código y resolver problemas cada vez mas complejos gracias al aumento de abstracción sobre un problema.

En este artículo se incluye como ejemplos la declaración de cuatro métodos de programación, un método void sin parámetros de entrada, otro método void con parámetros de entrada, un método con parámetro de salida pero sin parámetros de entrada y por último un método con parámetros de entrada y de salida.

¿Qué es un método en programación?

En programación, método se le llama a un bloque de código que tiene definido en su interior un conjunto de instrucciones, estas instrucciones realizan una determinada tarea. Cuando se necesita hacer uso de la función definida en el método simplemente se lo llama por su nombre, cuando el flujo del programa pasa por una llamada a un método el puntero salta a la región donde está definido el método, ejecuta todas sus instrucciones y al finalizar retorna a la línea posterior a la llamada al método.

Para definir un método hay que indicar si tendrá o no parámetros de salida, es decir si el método produce un dato como resultado de su ejecución y en caso afirmativo indicar ese tipo de dato. Luego se debe definir un nombre de identificación, por convención debe comenzar con la primera letra en mayúscula, además de intentar que el nombre sea representativo de la función que desempeña. Luego debemos indicar si tiene o no parámetros de entrada, en caso afirmativo, para cada parámetro de entrada incluir el tipo de cada uno y el nombre con el que se conocerá dicho parámetro dentro del método. Finalmente dentro del método se insertan todas las instrucciones de código a ejecutar, estas instrucciones pueden incluir la llamada a otros métodos, incluso un método puede llamarse a si mismo dando lugar a algoritmos recursivos, en este último caso, de no establecer bien la condición de finalización de las llamadas recursivas al método, tendríamos un método que se llama indefinidamente hasta que la pila de ejecuciones colapsa dando lugar al famoso error “Stack Overflow”.

Fig. 1: Esquema que ilustra las propiedades básicas de un método.

Módularización

En la figura 1 vemos al método representado como una caja, en la que aparece el nombre del método como título, se especifica el inicio y el final del método y el conjunto de instrucciones se encuentra dentro de esta región.

Para entender de qué se trata la modularización vamos a ver la figura 2. En la izquierda vemos representada una lista larga de instrucciones de programación.

Ahora dentro de esa lista hay cierto número de instrucciones que se utilizan en más de un lugar, las instrucciones A, B, C y D. Digamos que estas instrucciones realizan una tarea en concreto y siempre se ejecutan juntas.

Fig. 2: A izquierda un código con instrucciones repetidas.
A derecha el mismo código con módulos.

Supongamos que ahora debemos hacer cambios en esas instrucciones (por ejemplo para mejorar la manera en la que resuelve la tarea o agregar funcionalidad), lo que ocurre es que debemos identificar todas las regiones donde están esas instrucciones.

Seria genial si pudiésemos cambiar las cosas en un solo lugar y los cambios se apliquen en todas las regiones donde se usen estas instrucciones, ¿no?

Esto es precisamente lo que logramos con los métodos, nos permiten agrupar conjuntos de instrucciones en un módulo independiente, una caja negra por así decirlo, aquí es donde entra en juego el concepto de abstracción, sabemos que el método cumple una determinada función pero desde afuera no nos interesa realmente lo que hay en su interior, el método puede ser llamado cuando sea necesario sabiendo que cumplirá la función que le fue programada previamente. Como se observa a la derecha en la figura 2, las instrucciones ahora fueron reemplazadas por el nombre del método.

Parámetros de entrada y salida

En la figura 1 hay flechas que entran y salen del método. Con esto intento representar la entrada y salida de parámetros.

Podemos hacer que nuestro méteodo requiera parámetros de entrada, los cuales usamos en el interior para hacer cálculos o realizar cualquier tipo de tarea.

Además podemos hacer que la ejecución del método produzca un dato de salida, que usaremos fuera del método.

Métodos públicos y privados – Visibilidad

Por último en la figura 1 vemos un ojo normal y otro ojo tachado. Esto representa la visibilidad que tendrá el método, lo cual es un tema de programación orientada a objetos, no vamos a profundizar en este artículo pero en principio digamos que podemos tener métodos públicos y métodos privados.

Los métodos públicos podremos ejecutarlos desde otros Scripts por ejemplo, o podremos ejecutarlos desde un componente en la jerarquía en Unity, por ejemplo un botón.

Los métodos privados no serán accesibles desde contextos externos al Script donde está definido.

Sintaxis

La sintaxis es el conjunto de reglas establecidas en un lenguaje para escribir el código, en la figura 3 vemos posibles sintaxis de un método,

Fig. 3: Distintas combinaciones de parámetros de entrada y salida para los métodos.

Esta sintaxis se puede aplicar al lenguaje C# y Java, un detalle es que todos los métodos son públicos, pero podríamos reemplazar la palabra “public” por “private” y la sintaxis sería correcta.

Declaración de un método

Ejemplo 1 – Método void que no requiere parámetros

Analicemos el método que se encuentra arriba a la izquierda en la figura 3. Tenemos la estructura de un método público que no requiere parámetros y no devuelve parámetros.

Esta estructura la podemos encontrar por ejemplo en el script “GameControl” del proyecto Mi primer juego en Unity.
Trabajamos en este script en el video 4 de la serie.

Fig. 4: Método perteneciente al script: “GameControl”.

Observamos que ha sido declarado como privado en lugar de público, seguidamente encontramos la palabra “void” con la que indicamos que este método no va a devolver parámetros.
Luego el nombre del método: “placePlayerRandomly”.
Se abre y se cierran paréntesis con lo que se indica que este método no necesita que le demos parámetros.

El comienzo y final del método se indica utilizando llaves. En el interior estarán todas las instrucciones del método.

Invocación o llamada a un método

Una vez que el método está definido podemos hacer que se ejecute desde cualquier parte del script, incluso podemos invocarlo desde otros scripts.

En la siguiente figura se muestra la invocación al método “placePlayerRandomly” efectuada en el método “startGame” del mismo script “GameControl”.

Fig. 5: En amarillo: invocación al método “placePlayerRandomly”.

Para invocar este método se escribe su nombre y se abren y cierran paréntesis, cerramos la instrucción con punto y coma. Haciendo esto estamos logrando que se ejecuten todas las instrucciones del método.

Noten como los nombres de los métodos ya nos van ayudando a entender lo que está pasando. Al comenzar el juego se debe colocar el personaje aleatoriamente.

Desde el punto de vista de “startGame” el método “placePlayerRandomly” es una función que hace todo lo necesario para colocar el personaje aleatoriamente. El método “startGame” no sabe cómo es que lo hace exactamente pero no le importa, debido a que “placePlayerRandomly” hace bien su trabajo.

Ejemplo 2 – Método void con parámetros de entrada

En la esquina superior derecha de la figura 3 tenemos la estructura de un método que no devuelve parámetros pero si los requiere para funcionar.

Fig. 6: Método “writeTimer” del script UIManager.

Este método requiere dos parámetros para funcionar: un valor entero m que indicará los minutos del timer y un valor entero s que indicará los segundos.

Fig. 7: Invocación al método “writeTimer” desde el Script “Timer”.

La invocación de este método ocurre en varios lugares del código, en particular el método “addSeconds” perteneciente al Script “Timer”, este método recibe como parámetros un entero “s” que representa la cantidad de segundos que se deben agregar al timer debido a que el personaje agarró uno de los relojes que hay en el escenario.
El método “addSeconds” hace algunas operaciones con la variable “s” para actualizar el tiempo y finalmente ejecuta el método “writeTimer” que muestra los nuevos valores de tiempo en pantalla.

Dos observaciones: la primera es que como estamos ejecutando el método desde otro Script es necesario agregar “uiManager.” (prestar atención al punto) antes de escribir el nombre del método “writeTimer”.
La segunda es que los parámetros se ingresan dentro de los paréntesis y separados por coma.

En ambos scripts utilicé nombres confusos (m y s) a propósito para que en el futuro, cuando hablemos sobre contextos, nos sirva para analizar.

Ejemplo 3 – Método con parámetro de salida pero no de entrada

Pasamos a los métodos que devuelven parámetros.

El siguiente ejemplo que se corresponde con la estructura ilustrada en la esquina inferior izquierda de la figura 3, un método que devuelve un dato y no requiere parámetros para funcionar.

Fig. 8: Método “getRandomPosition” del Script “LabyrinthPiece”.

El dato que devuelve este método es un “Vector3” y se indica en la declaración, luego de la palabra “public”. Observen que la primer línea que no es un comentario dice: “Vector3 position;”, ahí estamos declarando un Vector3 que llamamos “position”, luego hacemos algunos cálculos y al final del método escribimos: “return position;” indicando que devolvemos ese Vector3.

Este método lo que hace es devolver un Vector3 (componente x,y,z) que representa una posición aleatoria que pertenece al interior de una determinada pieza de laberinto. De esta forma podemos usar esa posición para colocar el pedestal o los relojes sin preocuparnos por ejemplo de que aparezcan incrustados en una pared.

Fig. 9: Invocación al método “getRandomPosition”.

No es fácil entender la instrucción resaltada en la figura 9. Por ahora observemos dos cosas, la primera es que hacemos una llamada al método “getRandomPosition” utilizando su nombre (como se ve al final de la instrucción). La segunda es que el resultado de esa invocación es un dato Vector3, por lo tanto, utilizando el signo igual, lo asignamos al dato “rP” que se define como Vector3.

Ejemplo 4 – Métodos con parámetros de entrada y salida

El último ejemplo se corresponde con la estructura ilustrada en la esquina inferior derecha de la figura 3, un método que devuelve un dato y requiere parámetros para funcionar.

Fig. 10: Declaración del método “isBestScore”.

Vemos que en la declaración decimos que el método “isBestScore” devuelve un dato tipo bool y requiere un dato tipo int que llamamos “val”.

Si querés saber un poco sobre datos primitivos te invito a leer este artículo sobre variables o ver este video.

El método “isBestScore” devuelve una variable que puede valer verdadero o false (true or false). La utilidad de este método es que lo utilizamos para preguntar si la puntuación obtenida en la partida (val) es mejor que la que hay guardada hasta el momento (bestScoreValue).

Fig. 11: Invocación del método “isBestScore”.

La invocación a este método se hace en el método “gameWon”. Como el método devuelve un valor tipo bool, lo podemos usar directamente como argumento de una sentencia if, debido a que “score.isBestScore(time)” es equivalente a tener un valor verdadero o falso.

Método StartGame del Script GameControl

Para terminar la figura 12 se observa el método “startGame” junto con los demás métodos a los que va invocando.

Además de agrupar instrucciones, un método debe tener un propósito bien establecido y que sea coherente con el nombre que le dimos, de esta forma podremos saber rápidamente cúal es su funcion y qué tipo de instrucciones puede contener.

Fig. 12: Declaración del método “isBestScore”.

Por ejemplo el método “startGame” se encarga de coordinar una serie de acciones para comenzar el juego, pero no es él quien directamente realiza estas acciones, sino que invoca a otros métodos con tareas más específicas.

De esta forma nos resulta más simple realizar modificaciones en el código y depurar errores.

Conclusión

Los métodos constituyen una poderosa herramienta a la hora de elaborar soluciones específicas, eficientes, reutilizables y sostenibles en el tiempo.

Sostenibles en el tiempo porque en todas las etapas del desarrollo deberemos hacer cambios, de modo que tenemos que ser capaces de recordar o comprender rápidamente cómo funciona nuestro código.

Esto se logra utilizando nombres descriptivos, documentando nuestro código y aplicando el paradigma de orientación a objetos.

Vídeo tutorial sobre métodos en programación

En el siguiente vídeo vemos qué es un método, sus características, la sintaxis para definir un método y algunos ejemplos de métodos en programación.

Introducción

En este artículo vamos a explicar qué es una variable en programación, qué tipos de variables existen, para qué se usan y mostrar algunos ejemplos en lenguaje C# en el desarrollo de videojuegos en Unity.

¿Qué es una variable en programación?

El concepto de variable se utiliza en muchas disciplinas, el ejemplo más común quizás es la variable x en una función matemática, en este caso particular la palabra “variable” hace referencia a un elemento que puede tomar distintos valores, normalmente se suele tomar a X como la variable independiente y por lo tanto en una función matemática a cada valor de X distinto le corresponderá un valor de la variable dependiente Y. Pero en programación la palabra “variable” hace referencia a un concepto un poco distinto.

Para entender qué es una variable en programación vamos a ver un poco sobre memorias de computadoras.

Las memorias almacenan información en forma de registros de bits (es decir conjuntos de valores que pueden ser 0 o 1). Esta información será interpretada y utilizada por el procesador al correr nuestros programas. Las memorias a su vez cuentan con muchos de estos registros, así que cada registro tiene una dirección que permite encontrarlo, leerlo y escribirlo.

Entonces podemos decir que una variable es un DATO que se aloja en la memoria de la computadora. El nombre de identificación que le asignamos está asociado a la dirección dentro de la memoria y el valor que guardamos es la información que contiene la variable.

Ejemplos de variables según su tipo

En programación existen distintos tipos de variables, se utilizará un tipo de variable apropiada según la información que necesitemos almacenar. Tomemos en cuenta la siguiente tabla de información.

Fig. 1: Ejemplos de variables con nombre de identificación y valor.

La variable “edad” contiene el valor 22, esto quiere decir que cuando nosotros usemos la palabra “edad” en nuestro programa, indirectamente estaremos usando el valor 22. Hay que tener en cuenta de que el valor de una variable se puede modificar durante la ejecución de un programa.

Si hacemos la operación “2*edad” (2 multiplicado por edad), esa instrucción tendrá un resultado igual a 44.

Fig. 2: Ejemplos de variables con nombres MAL ELEGIDOS.

Elección de nombres para las variables

En la figura 2 se muestran variables que han sido definidas con nombres confusos. Esto suele pasar muy a menudo cuando queremos resolver los problemas rápidamente. Es importante elegir nombres representativos, porque luego de un tiempo olvidamos lo que hicimos y si necesitamos hacer cambios resultará difícil entender el código.

La variable “casa” de la figura 2, tiene el valor “falso”. Esta variable la pudimos haber utilizado, por ejemplo, para saber si un personaje está en su casa y si es verdadera restaurar su salud.

Cuando le damos un contexto, le encontramos sentido al nombre “casa” para identificar una variable de verdadero o falso, esto significa que el personaje no está en su casa. Sin embargo nos puede tomar un buen rato descifrar cuál era la función de la variable.

Podemos ahorrarnos el esfuerzo simplemente definiendo la variable como “personajeEnCasa”. Ahora el nombre de la variable es representativo de la función que cumple!

Tipos de Variables en Programación

Conozcamos las variables más básicas que vamos a usar para resolver nuestros problemas. Los ejemplos fueron tomados de la serie del laberinto.

Variables tipo Boolean (bool)

Las variables tipo “bool” reciben su nombre del Álgebra de Boole desarrollada por el matemático George Boole. Pueden encontrarse en 2 estados posibles: True o False (verdadero o falso). Las utilizamos para resolver operaciones lógicas.

Fig. 3: Fragmento del Script “GameControl” del proyecto Mi primer juego en Unity.

En el método que se ve en la figura 3, definimos el bool “validSelection” y lo inicializamos con el valor falso.

Dentro del bucle while se selecciona una pieza al azar del laberinto y se verifica si está lo suficientemente lejos del personaje.

Si la pieza de laberinto está muy cerca del personaje, validSelection sigue siendo false y el programa vuelve a elegir otra pieza de laberinto.

Si la pieza de laberinto está lo suficientemente lejos, validSelection se hará verdadera y el programa seguirá adelante utilizando esa pieza.

Fig. 4: El objeto que hay que encontrar en el laberinto es un pedestal con una espada.

Variables tipo Integer (int)

Las variables tipo “int” sirven para almacenar valores enteros positivos y negativos.


Fig. 5: Fragmento del Script “GameControl” del proyecto Mi primer juego en Unity.

En el método que se ve en la figura 4 se ven varios números enteros, “nPieces”, “labyrinthPieces.Length”, “nClocks”, los números 0 y 1 y la variable de iteración “i”.

Tomemos el caso de la variable iteradora “i”, está declarada e inicializada con valor 0 dentro del bucle for y sirve para ejecutar el método “placeAClock” (colocar un reloj), las instrucciones dentro del bucle se repetirán “nClocks” veces, siendo nClocks una variable entera que indica cuántos relojes deben haber en el escenario.

Por ejemplo si queremos que haya 10 relojes en el escenario, hacemos que “nClocks” valga 10, entonces el bucle for ejecutará 10 veces el método “placeAClock”.

Fig. 6:  Relojes que aparecen en el laberinto y dan tiempo extra al jugador.

Variables tipo Float

Las variables tipo float (punto flotante) nos sirven para representar números reales. Es decir además de números enteros positivos y negativos podemos representar números con parte decimal. El sistema de reprentación de punto flotante nos puede servir por ejemplo para representar magnitudes físicas.

Fig. 7:  Fragmento del Script “Lucy” del proyecto Mi primer juego en Unity.

Para la ocasión de Halloween definimos un Script para controlar el NPC Lucy, un espectro que mora en el laberinto. Lucy persigue al personaje con una determinada velocidad.

Para resolver este comportamiento hacemos una interpolación lineal entre la posición del personaje y la posición de Lucy utilizando como parámetro el float “velocity” multiplicado por el float “Time.deltaTime”.

Fig. 8:  Lucy persiguiendo al personaje.

Variables tipo String

Los datos de tipo string se utilizan para almacenar texto, no son una variable en si, sino instancias de una clase de programación, sin embargo muchos entornos de desarrollo nos permiten tratar los strings como variables primitivas.

Fig. 9:  Fragmento del Script “UIManager” del proyecto Mi primer juego en Unity.

El método “showMessage” que se ve en la figura 9 pertenece al script “UIManager”, este método recibe como parámetro un string llamado “message” y se encarga de mostrarlo en la interfaz de usuario, tal como se ilustra en la figura 10.

Fig. 10:  Ejemplo de mensaje que se muestra al interactuar con ciertos objetos.

Conclusión

En este artículo vimos qué es una variable en programación y cómo podemos usarla para representar distintos tipos de datos.

Una variable en programación es una referencia a un espacio en la memoria en el que está almacenado un dato. Podemos asignarle el nombre que queramos y al utilizar ese nombre estaremos haciendo referencia a ese dato almacenado en esa determinada parte de la memoria.

Vídeos sobre VARIABLES en programación

Qué son y para qué sirven las variables en programación


Cómo leer variables que están definidas en otro Script en Unity

En el canal tengo un vídeo que habla sobre cómo acceder desde un Script a una variable que está definida en otro Script y así poder leer su estado o modificarla si se necesita.

Información actualizada sobre esta entrada

Este artículo pertenece a una serie que consiste en hacer un juego simple en primera persona acerca de encontrar objetos dentro de un laberinto. Es una de mis primeras series de cuando empecé el canal, ahora he mejorado mucho este proyecto y puedes descargar el código fuente para importarlo en tu propio proyecto de Unity. Algún día vamos a hacer un remake de esta serie, suscríbete a mi canal para estar al tanto del nuevo contenido sobre Blender, Unity y programación.

Sígueme en itch.io y descarga el código fuente de este proyecto



PUEDES TESTEAR ESTE JUEGO AQUÍ. TAL VEZ TARDE UN POCO EN CARGAR
🔻

MOVEMENT: WASD CAMERA LOOK: MOUSE



Introducción al artículo original

En este artículo vamos a importar assets, es decir modelos 3D y texturas, agregarlos al motor Unity y configurarlos para que estén preparados para empezar a trabajar en el escenario. Vamos a crear GameObjects prefabricados que luego podremos reutilizar y vamos a modificar el cielo que viene por defecto, reemplazándolo por un Skybox donde aplicaremos las texturas que se descargaron de Asset Store en el video anterior.

Página principal del proyecto

Vídeo relacionado a este artículo

Importar assets, modelos 3D y texturas. Skybox




Importar archivos

Lo primero que vamos a hacer es descargar los archivos necesarios para el proyecto. Pueden hacerlo en el siguiente link.


Modelos 3D y Texturas

Dentro de estos archivos encontraremos modelos 3D para los muros del laberinto, un pedestal, una espada y un reloj. Cada modelo tiene sus propias texturas.

Fig. 1: Visualización de los archivos descargados.

Seleccionamos las carpetas y las arrastramos a la carpeta apropiada en Unity.

Esto lo hago primero seleccionando los archivos, luego haciendo clic para arrastrarlos (aparece un ícono fantasma indicando la cantidad de archivos que se está arrastrando) y luego aprentando la combinación ALT-TAB para cambiar de ventana, esto no hace que se pierda la selección, es por eso que podemos soltar los archivos en la carpeta de Unity.

Si hay problemas para hacer esto se puede minimizar la ventana de Unity o utilizar las opciones de importación de Unity.

Fig. 2: Las texturas han sido importados y se encuentran en la carpeta del proyecto.

Luego hacemos lo mismo con los modelos 3D. Hay que tener en cuenta las carpetas que creamos en el video anterior para poder organizarnos.

Fig. 3: Las texturas han sido importados y se encuentran en la carpeta del proyecto.

Modificar escenario

Ahora vamos a colocar todas los modelos 3D en el escenario para comenzar a darle forma al laberinto.

Primero eliminamos todos los elementos del escenario excepto el suelo. En el video anterior los habíamos colocados todos como hijos de un Empty GameObject llamado Escenario.

Fig. 4: Están seleccionados todos los objetos del escenario excepto el suelo.

El siguiente paso es tomar el modelo 3D llamado ElementosEscenario que importamos y arrastrarlo hacia la escena o la jerarquía. Esto colocará todas las piezas del laberinto en el escenario.

Fig. 5: Colocamos el Modelo 3D ElementosEscenario que viene en la descarga de esta página.

En el video anterior habíamos hecho que el suelo tenga un color marrón. Ahora vamos a aplicarle texturas así que hacemos que vuelva a ser de color blanco, de lo contrario nuestra textura tendrá un tinte marrón.

Fig. 6: Modificamos el color del material del suelo para que vuelva a ser blanco.

Aplicar texturas al suelo

Ahora vamos a utilizar las texturas que vienen dentro del zip que se puede descargar al inicio del artículo.

Comenzamos con el suelo, dentro de la carpeta tenemos tres texturas.

La textura Base Color se utiliza para darle color al material. Metallic determina el comportamiento que tiene el material frente a la luz y la textura normal también determina cómo son los rebotes de luz en el material, pero se utiliza para agregar detalles geométricos que aparentan una alta densidad de polígonos en los modelos 3D.

Fig. 7: Tres texturas correspondientes al suelo, en conjunto definirán al material.

Colocar texturas en los slots del material

Las texturas las agregamos en el inspector, para poder ver el material en el inspector tenemos dos opciones: podemos seleccionar el material directamente desde la carpeta del proyecto o seleccionando un modelo 3D del mundo que la tenga asignada.

Fig. 8: Visualización de los parámetros de un material Standard en el inspector.

En la figura 8 se observa que las texturas están asignadas en los slots del material.

El Slot Albedo determina el color del material, ahí colocamos la textura Base Color. La textura metallic en el slot metallic y la textura normal en el slot Normal map. Para esto último deberemos definir esa textura como un mapa de normales, hay dos formas de hacer esto, pero Unity nos da la opción en ese mismo lugar, como se observa en la figura 8 aparece un cartel que dice que esa textura no está definida como un mapa de normales y nos aparece un botón para arreglarlo. Hacemos clic en Fix Now.

Tiling de las texturas

En la esquina superior izquierda de la figura 8 se ve una porción del suelo. Noten que el suelo se ve borroso a diferencia de cómo debería ser (un suelo de cesped con margaritas).

Esto ocurre porque la textura está expandida hasta ocupar todo el plano, para cambiar esto debemos hacer que se repita más veces en la geometría. Esto se hace con el parámetro Tiling del material, en mi caso (por el tamaño del plano) los valores 100 para X y 100 para Y funcionan.

Fig. 9: Tamaño de mosaico. Esto hará que las texturas se distribuyan en mayor o menos escala dentro de la geometría.

Lo que va a ocurrir es que la misma textura se haga más pequeña y se coloque una a continuación de la otra. En la figura 10 se observa el cesped con margaritas. Noten como en la parte inferior de la imagen el suelo se ve bien, pero a medida que nos alejamos se vuelve más evidente el patrón de la textura, resulta obvio que es una misma textura que se repite.

Estamos usando una textura “Seamless” (sin costura), es decir una textura que al ponerla una a continuación de la otra (en los cuatro lados) encajan perfectamente y no se nota una línea de ruptura. Por más que la textura sea muy buena no podremos evitar que a la distancia se evidencie el patrón de repetición. Para evitar esto hay que usar más texturas, colocar objetos que interrumpan el patrón y demás.

Fig. 10: El material del suelo tiene un tamaño de mosaico adecuado. A la distancia se observa que la textura se repite.

En la figura 10 también se observa que el suelo refleja bastante luz, como si estuviese hecho de cristal o algo parecido. Para corregir esto disminuimos el valor de Smoothness, haciendo que el material refleje menos luz.

Fig. 11: Disminución del parámetro Smoothness del material para que refleje menos luz.

Fig. 12: Configuración final del material.

Extraer materiales de los modelos 3D

En versiones anteriores, cuando importábamos un archivo con extensión FBX, los materiales que pudieran estar definidos aparecían automáticamente junto con los modelos. Esto ha cambiado y creo que tiene sentido que tengamos que extraer manualmente los materiales, de esa forma podemos llevar un mejor control y evitar tener muchas copias del mismo material.

Hay dos formas de extraer el material, al menos en la versión al momento de grabar el video.

En este caso hacemos clic sobre el modelo importado y en el inspector vamos a la pestaña materiales y hacemos clic en: “Extract from prefab”.

Fig. 13: Extracción de los materiales del archivo importado desde el inspector.

Aparece una ventana para seleccionar la ubicación de los materiales extraídos, si se quiere podemos crear una carpeta para los nuevos materiales.

Fig. 14: Guardar materiales extraídos manualmente del material.

Los nuevos materiales aparecen en la carpeta seleccionada.

Fig. 15: Visualización de los materiales extraídos en la carpeta del proyecto.

Aplicar texturas a los nuevos modelos 3D

El siguiente paso es hacer lo mismo que hicimos con el material del suelo, vamos a ir material por material agregando las texturas a los respectivos slots.

Hay un problema que surge cuando tenemos que hacer este tipo de tareas y es que cuando seleccionamos algo en la jerarquía o la carpeta del proyecto, el inspector nos muestra información sobre esa selección. En nuestro caso sería bueno que el inspector quede bloqueado mostrando el material que estamos configurando.

Para esto está el candadito pequeño que se ve en la esquina superior derecha de la pestaña inspector. Haciendo clic en él bloqueamos lo que muestra el inspector y podemos navegar libremente por las carpetas del proyecto sin perder de vista nuestro material. Cuidado que esto también puede ser un problema, a veces se nos olvida el inspector bloqueado y no sabemos por qué no podemos ver información sobre lo que seleccionamos.

Fig. 16: Colocación de las textoras en los materiales. Al seleccionar una pieza de la jerarquía, en el inspector aparecen sus componentes, entre ellos el material que tienen asignado.

Otra alternativa para este tipo de tareas es modificar el layout de Unity para nuestro beneficio, es decir abrir más pestañas del proyecto y bloquearlas para que cada una muestre una cosa.

En la figura 16 se observa que una de mis ventanas del proyecto muestra los materiales, la otra las texturas y el inspector muestra el material seleccionado. Esto hace que el proceso de agregar muchas texturas a los materiales sea más rápido.

Fig. 17: Aprovechando la flexibilidad de las ventanas de Unity y la opción de bloquearlas para facilitar el proceso de configuración de los materiales.

Creación de GameObjects prefabricados

Una vez que hemos configurado todos los materiales, el siguiente paso es crear los “Prefabs” es decir GameObjects prefabricados que posteriormente podremos instanciar durante el diseño o en tiempo de ejecución del juego. Por ejemplo más adelante haremos que los relojes aparezcan aleatoriamente en el escenario. Para poder lograr eso necesitamos contar con un GameObject prefabricado del cual se crearán clones.

Fig. 18: Proceso de creación de Prefabs. Primero seleccionamos un GameObject de la jerarquía.

Para crear un GameObject prefabricado simplemente lo tomamos de la jerarquía y lo arrastramos a cualquier carpeta de nuestro proyecto, como se observa en las figuras 18 y 19.

Fig. 19: Proceso de creación de un prefab. Segundo: tomamos el GameObject y lo arrastramos a alguna carpeta del pryecto.

Luego de eso nos aparece el Prefab en la carpeta, como una miniatura que muestra su apariencia y el nombre. A partir de ahí podemos tomarlo y arrastrarlo hasta la escena para crear todas las copias que necesitemos, como se observa en la figura 20.

Fig. 20: El prefab se encuentra en la carpeta del proyecto y puede ser reutilizado.

Repetimos este proceso para cada pieza individual del laberinto, el pedestal y los portales.

Fig. 21: Se han creado prefabs para todos los modelos importados. De esta forma podremos construir el laberinto y colocar los objetos.

Definir un nuevo Skybox

Ahora vamos a modificar el cielo estándar de la escena. Utilizaremos los archivos descargados de la tienda Asset Store del video anterior, hacer esto era un paso opcional, así que si se tienen los archivos no hay problema, podemos seguir avanzando.

Primero creamos un nuevo Material, lo llamamos “Cielo” y lo seleccionamos para visualizar sus propiedades en el inspector.

Fig. 22: Modificación del Shader para definirlo como Skybox de seis lados.

Luego hacemos clic en el menú desplegable Shader que por defecto tiene seleccionado “Standard Shader”, en su lugar elegimos el Shader “6 Sided Skybox”. Esto cambiará totalmente las propiedades del material en el inspector, ahora se observa un inspector como el de la figura 23.

Fig. 23: Shader tipo skybox de seis lados visualizado en el inspector.

Asignaremos las texturas del paquete Skybox descargado de Asset Store al slot correspondiente del Skybox.

Fig. 24: Las texturas descargadas en el video anterior se asignan a los slots del material en el inspector.

Luego vamos al menú Window > Lighting > Settings para abrir la configuración de la iluminación.

Fig. 25: Se abre la ventana de configuración de la iluminación.

Se abre una ventana como la que se ilustra en la figura 26, la cual coloco al lado del inspector para que no esté flotando.

Fig. 26: Visualización de los parámetros de configuración de la iluminación.

Hacemos clic en el ícono del círculo con el punto de “Skybox Material” (sobre el que está el cursor en la figura 26) y en la ventana que aparece elegimos el Skybox que acabamos de configurar (figura 27).

Fig. 27: Ventana de selección para el material del Skybox. Se selecciona el material creado anteriormente.

Inmediatamente vemos como el cielo que viene por defecto es remplazado por un nuevo cielo de un atardecer con nubes. Con las imágenes apropiadas podemos crear un cielo personalizado para nuestro proyecto.

Fig. 28: El cielo de la escena cambia, ahora se observa un atardecer nublado.

Ajustar iluminación

En la figura 28 se observa que la luz es demasiado pálida, es decir no parece la luz que se vería en un atardecer. Para corregir esto seleccionamos la fuente de luz en la jerarquía para ver sus propiedades en el inspector (se observa un inspector análogo al que se ve en la figura 33).

En el inspector seleccionamos un color acorde al tono del cielo y ajustamos la intensidad a gusto.

Luego modificamos la rotación de la fuente de luz para que coincida con la posición del sol en el Skybox.

Fig. 29: Se modifica la rotación de la luz direccional para que coincida con la posición del sol en el Skybox.

En este punto del video hago una carpeta para colocar los prefabricados pertenecientes al escenario y otras para los objetos. De nuevo hago hincapié en la organización del proyecto.

Fig. 30: Los prefabs se colocan en carpetas con nombres significativos para mejorar la organización.

Finalmente se me ocurre agregar una segunda fuente de luz que apunte directamente hacia abajo y no produzca sombras, de esa forma la escena no se ve tan oscura.

Fig. 31: Se ha cambiado el color de la luz direccional para que coincida con el tono del Skybox y se ha duplicado agregar luz en las zonas oscuras.

Selecciono la fuente de luz de la jerarquía y presiono CTRL+D para duplicarla, luego la oriento de manera perpendicular al suelo.

Fig. 32: La nueva luz direccional se orienta de forma perpendicular al escenario para que ilumine de manera general.

En el inspector ajusto los parámetros y en “Shadow Type” selecciono la opción “No Shadows”, como se observa en la figura 33. Con esto finalizamos el video 2 de la serie Mi Primer Juego en Unity.

Fig. 33: Con la nueva luz direccional seleccionada, en el inspector hacemos que no produzca sombras.

Conclusión

En este video hemos trabajado bastante en el aspecto. Agregamos modelos 3D y texturas, con lo cual aprendimos un poco más sobre los materiales. En el caso de los modelos 3D importados, debemos extraer manualmente los materiales que puedan traer o colocarle materiales nuevos, esta parte está muy conectada con el diseño 3D de la pieza, es decir los materiales que definamos en nuestro software de diseño 3D aparecerán en Unity, quizás tengamos oportunidad de profundizar en este tema más adelante.

Vimos que podemos crear prefabricados de los GameObjects que se encuentran en la jerarquía. Estos prefabricados podremos colocarlos en el mundo fácilmente, en el momento de la edición o en tiempo de ejecución del juego.

Modificamos el cielo que viene por defecto en la escena, en su lugar creamos un material tipo Skybox en el que colocamos texturas para representar los seis lados de la “caja del cielo”, esto se comporta como si estuviésemos dentro de un cubo gigante cuyo horizonte se encuentra muy lejos.

Finalmente mejoramos un poco la iluminación de la escena, corrigiendo el color y agregando otra fuente de luz que sume en intensidad pero no produzca sombras.

Introducción

Modelar un rostro 3D puede resultar una tarea desafiante, especialmente cuando recién empezamos en el mundo 3D. En este artículo vamos a ver una forma fácil y práctica de hacer un rostro 3D con una topología bien lograda utilizando Blender, aunque la técnica se puede usar en cualquier software de diseño 3D.

La idea es partir de una imagen de referencia en la que se cuente con la vista frontal y la vista lateral de la pieza que se quiere modelar, pero además que la imagen de referencia muestre información sobre la topología del objeto, es decir dónde están ubicados los vértices, aristas y caras, esto nos permite crear fácilmente el modelo 3D, en este caso el modelo de la cara. Es una práctica muy buena de modelado 3D, especialmente si recién estás empezando, ya que se hacen tareas muy repetitivas y constantemente se está cambiando de vista y de herramientas, puede ayudarte a agilizar el manejo del software.

VÍDEO: Procedimiento para recrear una cara en 3D en Blender a partir de una imagen de referencia

VÍDEO: Timelapse del modelado 3D del rostro en Blender

Topología

La topología es la disciplina que estudia los objetos y sus propiedades geométricas. En los modelos 3D hace referencia al conjunto de vértices, aristas y caras y su interrelación. Es importante que nuestros modelos 3D tengan una buena topología, de esa forma los modificadores, la deformación proporcional, texturización y otras funciones tendrán mejores resultados.

Una buena topología se consigue modelando las piezas con caras de cuatro vértices (quads en inglés) y creando “Edge Loops” que encajen con las curvas naturales del objeto que queremos modelar. En la figura 1 se observan esas caras y edge loops y cómo están distribuidos en el modelo.

En la medida de lo posible hay que evitar el uso de N-gons, que es el nombre con el que se suelen llamar a los polígonos que tienen mas de cuatro aristas.

Fig. 1: Imagen de referencia para topología de rostro 3d.
Fuente: Blender Artist.

¿Cómo modelar en Blender a partir de una imagen de referencia?

En Blender podemos colocar imágenes de referencia que se mostrarán de fondo en las vistas ortográficas (las que se acceden con las teclas 1, 3 y 7 del teclado numérico).

En el menú de propiedades (acceso directo tecla N) hay una sección que se llama “Background Images”, activamos esta opción y seleccionamos la imagen de referencia. Podemos moverla, rotarla y escalarla, además podemos poner más de una imagen de referencia, como se hace en el video.

Reutilización del modelo del rostro 3D para otro personaje

Marittie es un personaje chibi que hice a partir de un dibujo que hizo mi novia hace algún tiempo.

Fig. 2: Imagen de referencia para el modelo de Marittie.

Al momento de hacerlo tenía un año de experiencia con Blender y aún no estaba familiarizado con el concepto de topología. Sin embargo logré hacer un modelo y en su momento me gustaba mucho.

Fig. 3: Render del antiguo modelo de Marittie.

La topología por supuesto no era buena. El modelo tenía un número muy alto de polígonos, muy mal optimizado. La animación era difícil de lograr. Había irregularidades difíciles de corregir.


Fig. 4: Otro render del modelo antiguo.

Conforme pasó el tiempo fui aprendiendo a mejorar la geometría y llegó el tiempo de rehacer el modelo. Hacía poco tiempo que había hecho el render de la máscara Veneciana (el que se muestra en el timelapse de arriba) y se me ocurrió que podía tomar como base ese modelo para rehacer al personaje.
Hice una copia de la máscara y comencé a aplicar deformaciones proporcionales. Como la topología de la cara era buena, fui capaz de transformar la máscara veneciana en el nuevo rostro de Marittie.

Fig. 5: Render del nuevo modelo.
Fig. 6: Otro render del nuevo modelo.

Fig. 7: Comparación del modelo antiguo y el modelo nuevo.

Conclusión

La mejora en la topología del nuevo modelo hace más fácil la animación. La herramienta de deformación proporcional tiene mejores resultados y se pueden lograr expresiones muy lindas.

Información actualizada sobre esta entrada

Este artículo pertenece a una serie que consiste en hacer un juego simple en primera persona acerca de encontrar objetos dentro de un laberinto. Es una de mis primeras series de cuando empecé el canal, ahora he mejorado mucho este proyecto y puedes descargar el código fuente para importarlo en tu propio proyecto de Unity. Algún día vamos a hacer un remake de esta serie, suscríbete a mi canal para estar al tanto del nuevo contenido sobre Blender, Unity y programación.

Sígueme en itch.io y descarga el código fuente de este proyecto



PUEDES TESTEAR ESTE JUEGO AQUÍ. TAL VEZ TARDE UN POCO EN CARGAR
🔻

MOVEMENT: WASD CAMERA LOOK: MOUSE



Introducción al artículo original

Este es el comienzo de la serie del Laberinto, en la que crearemos un prototipo simple de juego en primera persona en el que el escenario será un laberinto, mientras vamos viendo todas las herramientas con las que cuenta Unity para crear juegos.

Esta serie es ideal para aquellos que no tienen experiencia con Unity ya que se muestra de todo un poco y sin entrar en detalles, el propósito es mas bien observar todo el proceso de desarrollo para tener una idea general.

Página principal del proyecto

Vídeo relacionado a este artículo




Creación del proyecto

Al ejecutar Unity o Unity HUB, nos aparece una ventana en la que podemos crear nuevos proyectos, abrirlos y acceder a los tutoriales.

Vamos a crear un nuevo proyecto 3D, le damos un nombre y elegimos una ubicación en el disco. Todo lo demás es opcional. Cuando todo esté listo creamos el proyecto

Le damos un nombre y seleccionamos el template 3D.

Fig. 1: Ventana de inicio de Unity. Crear o cargar un proyecto.

Lo primero que vamos a hacer es crear una nueva escena. En esta versión de Unity, por defecto se nos crea la escena “SampleScene.unity”, como se observa en la cabecera del programa en la figura 2.

Para crear una nueva escena hacemos clic en File > Save Scene As…

Fig. 2: Para guardar una nueva escena vamos a File > Save Scene As…

Le damos un nombre y la guardamos. Al hacerlo se observa en la cabecera el nombre de la nueva escena.

Fig. 3 Ventana emergente para guardar la escena.

Cada escena tiene su propio diseño independiente de las demás. Por ejemplo podemos usar una escena para el menú principal y otra para el juego.

Todo va a depender de cómo queramos estructurar el juego.

Creación de carpetas

Es importante establecer un sistema de organización para los archivos de nuestro juego en Unity desde el comienzo, porque a medida que avanzamos vamos creando más Scripts, importando más archivos y las cosas se desordenan fácilmente.

Todos los archivos del proyecto se encuentran en la ventana “Project”, dentro de una carpeta que se llama “Assets”. Esta carpeta se puede encontrar en el directorio que definimos para el proyecto.

Fig. 4: Crear una nueva carpeta para el proyecto.

Como se observa en la figura 4, al hacer clic derecho se despliega un menú con distintas opciones, vamos a la opción “Create” y elegimos “Folder”.

Creamos tantas carpetas como sea necesario para organizarnos, esta parte es personal,

Importar paquetes

Para empezar a hacer un juego en Unity no es necesario crear todo desde cero, podemos valernos de las librerías existentes para comenzar a trabajar en una idea. A continuación vamos a importar Unity Packages (paquetes de Unity) de la librería Standard Assets y de la Asset Store.

Standard Assets – Unity

En la versión de Unity que tenía al momento de grabar el video, se podía importar los paquetes de Standard Assets, una librería que ofrece Unity que cuenta con distintos prefabricados, efectos y scripts que resultan útiles al empezar.

Haciendo clic derecho en la carpeta tenemos la opción “Import Package” donde podemos elegir un paquete que tengamos descargado usando la opción “Custom Package” o podemos elegir algún paquete de Standard Assets.

Si no les aparecen todos los paquetes como se ve en la figura 5, tienen la opción de descargarlos de Asset Store, pero deben tener una cuenta en Unity para poder hacerlo. Más adelante hablamos de eso.

Fig. 5: Importar paquetes de la librería Standard Assets.

En este caso voy a importar de Standard Assets los paquetes “Characters”, “ParticleSystems” y “Utility”.

Con el paquete “Characters” podremos colocar en el escenario un prefabricado que tiene implementado un control en primera persona.

ParticleSystems trae algunos efectos interesantes para utilizar, como fuego y humo.

Utility trae algunos scripts útiles como un contador de frames por segundo (fps).

Asset Store – Unity

La tienda virtual de Unity contiene una infinidad de Assets, algunos de pago y otros gratuitos.

Para acceder a la tienda utilizamos la pestaña Asset Store de Unity, si no se encuentra abierta, vamos a Windows > Asset Store o usamos el acceso directo Ctrl+9. Como se observa en la figura 6.

Fig. 6: Abrir la tienda de Assets desde la ventana Window.

Para utilizar la tienda debemos iniciar sesión en nuestra cuenta (podemos hacerlo desde Unity). Luego utilizando la pantalla de inicio o la barra de búsqueda podemos explorar el contenido.

Con el propósito de ver cómo se usa Asset Store vamos a descargar dos paquetes. El primero se llama “SkyBox” y el segundo “FastMobileBloom”, usamos la barra de búsqueda para encontrarlos, en las figuras 7 y 8 se observan las miniaturas de los paquetes.

Fig. 7: Paquete Skybox de la tienda de assets.

Fig. 8: Paquete FastMobileBloom de la tienda de assets.

Para agregarlos a nuestro proyecto entramos en el paquete y ponemos “Descargar”, al finalizar la descarga nos aparece la opción “Importar”.

Fig. 9: Al entrar en un paquete podemos descargarlo e importarlo, leer comentarios y más.

Jerarquía del proyecto

Dentro de cada escena estarán definidos todos los elementos del juego, es decir un escenario, objetos que forman parte del mundo, objetos invisibles que sirven para la lógica del juego, interfaz gráfica, etc. Todos estos elementos son conocidos como GameObjects y estarán listados en la jerarquía de nuestro proyecto.

Como se observa en la figura 10, al crear una nueva escena, tenemos dos GameObjects que vienen por defecto. Una cámara y una luz direccional.

Fig. 10: Jerarquía del proyecto. Por defecto tenemos una cámara y una luz direccional.

Vamos a eliminar la cámara y empezar a agregar objetos al escenario.

Haciendo clic derecho en la jerarquía tenemos la opción de agregar distintos GameObjects, por ejemplo objetos vacíos, primitivas, luces, etc.

Fig. 11: Haciendo clic derecho en la jerarquía podemos crear nuevos objetos.

Voy a armar un escenario simple con un plano para el suelo y algunos cubos y cilindros.

Fig. 12: Plano creado desde la jerarquía.

A medida que vamos agregando más objetos al escenario la jerarquía va creciendo. También es recomendable tener una buena organización desde el principio, colocando nombres apropiados y usando Empty GameObjects para agrupar los objetos.

Fig. 13: Agregando objetos al mundo.

Observen las figuras 14 y 15. En la primera se ve que ambos cilindros están contenidos por el cubo, ya que están ligeramente más a la derecha y el cubo tiene una flecha para desplegar y colapsar el contenido. En la figura 15 en cambio se observa que están al mismo nivel por así decirlo.

Fig. 14: El cubo es el padre de los cilindros.

Fig. 15: El cubo y los cilindros están al mismo nivel jerárquico.

Esto representa la relación de parentesco que existe entre los GameObjects. En la figura 14 se dice que el cubo es el GameObject padre y ambos cilindros son hijos. Esto implica que los cilindros van a estar afectados por ciertos cambios que se apliquen al padre. Por ejemplo si desactivamos el cubo, ambos cilindros también se desactivarán.

Lo mismo ocurre con la componente transformación por ejemplo. La posición de los cilindros se mide respecto de su padre, por lo tanto si movemos al padre, los cilindros se mueven en conjunto.

Materiales

Para aplicar colores y texturas sobre los objetos del mundo se utilizan los materiales. Vamos a crear algunos para ver cómo funcionan. Clic derecho en alguna carpeta del proyecto luego Create > Material, como se observa en la figura 16.

Fig. 16: Proceso de creación de materiales.

El material se visualiza como una esfera en donde están aplicadas las propiedades del material debemos asignarle un nombre, como siempre mantener el orden desde el primer momento nos beneficia a largo plazo.

Fig. 17: Visualización de los materiales en la ventana del proyecto.

Los materiales tienen varios parámetros que lo definen, en nuestro caso simplemente vamos a modificar el color haciendo clic en el recuadro a la derecha de “Albedo”. Al hacerlo aparece una paleta de colores donde podemos elegir el color o escribir su código hexadecimal.

Fig. 18: Visualización de las propiedades de los materiales en el inspector.

Para aplicar un material simplemente lo arrastramos sobre el objeto deseado.

Fig. 19: Se ha aplicado un material al plano.

Iluminación

Vamos a seleccionar la luz direccional desde la jerarquía o en la ventana escena.

Fig. 20: Se ha aplicado materiales a todos los objetos de la escena.

Por luz direccional se entiende una fuente de luz como el sol, es decir situada en el infinito y que envía todos sus rayos paralelos. Por esta razón mover la fuente o hacerla más grande no afecta a la iluminación de la escena, pero cambiar la rotación si lo hace. Lo vemos en la dirección de las sombras.

Fig. 21: Rotación de la luz direccional, las sombras se proyectan en otra dirección si comparamos con la figura 20.

Crear una escalera con primitivas

Vamos a hacer este pequeño ejercicio para ver los elementos de la barra de herramientas que tiene Unity, en particular las herramientas que nos permitirán modificar la componente Transform de los GameObjects. Comenzamos creando un cubo en la jerarquía.

Fig. 22: Barra de herramientas básicas de Unity.

Las herramientas mover, rotar y escalar se observan en la figura 22 en las posiciones 2, 3 y 4 respectivamente y se acceden rápidamente usando las teclas “W”, “E” y “R” (respectivamente).

Si apretamos la tecla R teniendo seleccionado el cubo, podremos escalarlo utilizando el “Gizmo” de los tres ejes, como se observa en la figura 23.

Fig. 23: Escalamiento de un GameObject en una dirección.

Lo escalamos en dos direcciones para darle la apariencia de un escalón.


Fig. 24: Escalamiento de un GameObject en una dirección.

Le aplicamos un material y con la tecla W podemos ubicar el escalón en el escenario.

Fig. 25: Posicionamiento del objeto en la escena.

Podemos duplicar fácilmente el objeto utilizando el acceso directo CTRL+D y con la tecla W elevarlo un poco y desplazarlo hacia atrás.

Fig. 26: Duplicación de un GameObject.

Podemos repetir este proceso con varios escalones seleccionados.

Fig. 27: Duplicación de un conjunto de GameObjects.

Vamos a crear un Empty GameObject y llamarlo “Escenario”.

Fig. 28: Creación de un Empty GameObject para organizar elementos.

Fig. 29: Al Empty GameObject se le da el nombre Escenario.

Luego seleccionamos todos los objetos y los arrastrámos hacia este nuevo objeto para agruparlos. De esta forma podemos colapsar todo el contenido y la jerarquía se ve más ordenada, como se observa en la figura 30.

Fig. 30: Todos los GameObjects del escenario se arrastran dentro del Empty GameObject y se colapsa el contenido.

Controlador de primera persona

La razón por la que previamente eliminamos la cámara es porque vamos a colocar en la escena el prefabricado “FPSController” de Standard Assets y este ya trae una cámara incorporada. Este prefab se encuentra en el directorio Standard Assets > Characters > FirstPersonCharacter > Prefabs.

Fig. 31: Prefab FPSController del paquete Characters de Standard Assets.

Lo llevamos a la escena y lo acomodamos donde nos guste.

Fig. 32: Se coloca el prefab FPSController en la escena.

Bugs

Ya estamos en condiciones de hacer algunas pruebas, en la parte superior al centro tenemos tres botones.

El botón Play se utiliza para empezar a correr el juego. El segundo botón pausa el juego pero no lo finaliza. El tercer botón sirve para avanzar un sólo frame en el juego.

Al pulsar el botón Play aparece un cartel de error que dice que todos los errores deben arregllarse antes de poder entrar en el modo juego.

Fig. 33: Al intentar entrar en el modo juego aparece un cartel de error.

Este es un problema con el que nos vamos a encontrar muy seguido, especialmente al principio y suele ser muy frustrante, más aún si estamos aprendiendo a programar.

Para obtener información sobre lo que está pasando tenemos la ventana “Console”. Si no se encuentra abierta, la encontramos en el menú “Window”.

Fig. 34: En la consola se observan varias advertencias con un cartel amarillo.

En la figura 34 observamos una gran cantidad de advertencias pero esto no afecta el funcionamiento del juego, son sugerencias para optimizar el código, advertencias por uso de código obsoleto y demás. Si sólo tuviésemos advertencias podríamos entrar en el modo juego sin problema.

La figura 35 es una captura luego de hacer clic en el botón “Clear”, esto limpia las advertencias y deja ver los errores que no se pueden pasar por alto para poder entrar en el modo juego. Estos se muestran con un cartel Rojo.

Fig. 35: En la consola se observa un mensaje de error con un cartel rojo.

No hay mucha información sobre el error pero nos basta para entender que hay un archivo que no se encuentra. Si observamos la dirección vemos que está relacionada al paquete Standard Assets y vemos que su dirección comienza con “Assets > Standard Assets > …”. En el video hicimos una carpeta llamada “Paquetes Externos” y dentro de ella colocamos el paquete Standard Assets.

Todo parece indicar que hay algún proceso que está buscando archivos de Standard Assets pero dentro de la carpeta Assets, no la carpeta Paquetes Externos, así que muevo la carpeta Standard Assets a la carpeta Assets y el error se soluciona. Ahora si estamos en condiciones de entrar en el modo juego.

Fig. 36: Se lleva el paquete Standard Assets a la carpeta Assets del proyecto.

Prueba del juego en Unity

El prefab FPS Controller funciona como un juego estándar en primera persona. Avance, retroceso, desplazamientos laterales con las teclas WASD o las flechas. Salto con barra espaciadora, con Shift se puede correr. Con el mouse se controla la vista y tiene sonido de pasos. Es un buen punto de partida para empezar a formar una idea.

Fig. 37: Luego de corregir los errores se puede entrar en el modo juego.

Compilación

Ahora vamos a generar una versión standalone del proyecto, es decir un conjunto de carpetas y archivo ejecutable que puede funcionar en nuestro sistema operativo sin necesidad de tener el motor Unity abierto.

Vamos a la pestaña File y hacemos clic en Build Settings, para configurar los parámetros de compilación.

Fig. 38: Para hacer una compilación hacemos clic en File > Build Settings.

Nos sale la ventana que se ilustra en la figura 39, allí podemos seleccionar la plataforma objetivo.

Si observan en la parte superior es donde están listadas las escenas del juego. En ese momento no tenemos ninguna escena agregada a la compilación.

Fig. 39: Ventana con parámetros de compilación. No se han agregado escenas.

Haciendo clic en el botón Add Open Scenes vamos a agregar la escena en la que estuvimos trabajando en todo el video. Como se observa en la figura 40.

Fig. 40: Ventana de compilación. Se ha agregado la escena en la que trabajamos.

Ahora solo resta hacer clic en Build And Run y elegir la carpeta donde colocar los archivos generados. Al finalizar el proceso de compilación el juego se ejecuta y nos aparece una ventana en la que podemos seleccionar la resolución y calidad. Le damos al botón Play! y el juego comienza.

Fig. 41: Ventana de inicio del juego. Se puede elegir la resolución y la calidad.

Conclusión

En esta primera entrada vimos los primeros pasos al comenzar un juego en unity. Hicimos bastante énfasis en la organización, que nos beneficiará a largo plazo en el desarrollo.

Vimos las escenas de Unity, cada escena es un mundo aparte y contiene una determinada cantidad de elementos o GameObjects que se encuentran listados en la jerarquía del proyecto.

Importamos archivos tanto de la librería Standard Assets como de la tienda Asset Store. Al principio lo más importante es tratar de darle forma a las ideas que tenemos, para esto los assets de las librerías nos facilitan las cosas.

Vimos algo sobre la relación de parentesco que existe entre los GameObjects y cómo los cambios que se aplican sobre un objeto padre también impactan en sus objetos hijo.

Creamos materiales que sirven para dar color y textura a los objetos.

La fuente de luz direccional nos sirve para iluminar una escena como lo haría el sol, las sombras se proyectarán en función de la rotación que tenga esta fuente de luz.

Hicimos una escalera simple utilizando las primitivas que se pueden crear con Unity y aplicándoles transformaciones, es decir cambios de escala, rotación y posición.

Colocamos el prefabricado de Standard Assets “FPSController” que es un control en primera persona. Rápidamente ya tenemos un personaje que puede recorrer el escenario, correr y saltar.

En el proceso hubo errores que debimos solucionar para poder entrar en el modo juego. La depuración de errores es una tarea difícil cuando empezamos a programar, pero con la práctica se van cometiendo menos errores y resulta más fácil corregirlos.

Finalmente hicimos una compilación ejecutable desde el sistema operativo. Existe una gran cantidad de plataformas a las que podemos exportar nuestro juego en Unity. En principio vamos a exportar a Windows y Android.

Introducción

Mucho antes de empezar con la programación y el mundo de los videojuegos solía ser carpintero, en ese período de mi vida fue cuando cree junto a un amigo el infame gabinete para PC hecho en fibrofácil.

Historia

Llevaba un tiempo fabricando cosas en mdf o fibrofácil como se conoce en donde vivo, había hecho varios sets de ajedrez y unas cajas que mi novia había decorado con decoupage, me gustaba bastante y tenía facilidad. Un día estaba en clase de química en la facultad y le muestro a un amigo los trabajitos que había hecho. Él se interesa y me cuenta que necesitaba una caja provisional para tapar su computadora ya que aún no había conseguido un gabinete y su computadora la cubría con una caja de cartón.

En la figura 1 se muestra la estructura que tenía hasta el momento para la placa madre y fuente de alimentación.

Fig 1: Estructura de soporte.
Fig 2: Disposición inicial de los elementos. Abajo fuente de alimentación y disco rígido.

Entonces la idea era hacer una caja que contenga eso para prevenir que se ensucie.

Diseño del gabinete MDF

Mi amigo hizo un diseño que le parecía conveniente para la ventilación y a partir de eso hice un diseño 3D usando SolidWorks (en ese momento no conocía Blender), esa fue mi primera aproximación con el modelado en 3D sin contar que de chico hacía mapas para el Counter Strike usando Entidad 3D.

Con el diseño 3D pudimos hacer unos renders y ajustarlo hasta que quedó como mi amigo quería. A partir de ese diseño 3D hice un plano de 6 vistas para los cortes de las piezas (creo que por ese tiempo también había hecho dibujo técnico en la facultad).

Fig 3: Pieza de mdf y plano de los cortes.

Tomando como base los planos, dibujé las seis piezas sobre una placa de mdf de 5 mm de espesor, en esta parte se tomaron bien las medidas para que todo quede a escuadra.

Fig 4: Con todos los cortes dibujados, se hacen agujeros para que entre la hoja de la caladora.

Hice todos los agujeros en los cortes internos para que entre la hoja de la caladora.

Fig 5: Corte de un lateral.

Los cortes simples los hice utilizando una caladora de mano y los cortes precisos con una caladora de banco.

Fig 6: Corte interior de una pieza lateral del gabinete mdf.

Los cortes quedaron a medida, solo que no tan rectos porque fueron hechos a pulso.

Fig 7: La primer pieza del gabinete mdf, uno de los laterales.

En la figura 8 se ve que la estructura ya iba tomando forma.

Fig 8: Las piezas del gabinete cortadas sin encolar.

Pegué las tres piezas usando cola y luego apliqué un prensado para que la estructura quede bien firme, a continuación se observan las herramientas de prensado.

Fig 9: Encolado y prensado del gabinete mdf.

Observen como la prensa roja de metal (una prensa propiamente dicha) se encarga de mantener armada la caja de Monopoly, de esa forma la estructura no colapsó bajo su propio peso.

Fig 10: Diseño para las bahías de los discos.

Para las bahías de los discos hicimos una estructura de tres pisos, en la siguiente figura se muestra la idea para el diseño.

Fig 11: Bahías de los discos sin pintar.

La pieza la construimos también con mdf de 5 mm y una capa de pintura acrílica roja.

Fig 12: Bahías de los discos acabada.

Luego empezamos a pensar en la decoración de la caja, porque tenía que tener aunque sea una capa de pintura. En ese momento no sabía tanto de componentes de computador y marcas como ahora, sin embargo propuse algunos diseños que le gustaron.

Fig 13: Garabateando algunos diseños mientras hacía mi tarea de Alemán.

Fig 14: El gabinete mdf luego del encolado y con parte del diseño. Las piezas azules son acrílico transparente.

El logo de Republic of Gamers para una placa MSI.

Fig 15: Lateral del gabinete mdf, noten el acrílico colocado en una ranura en la parte superior.

Habíamos comprado unas empanaditas de carne para comer y se estaba haciendo tarde.

Aquí se puede ver que estoy dando los últimos retoques. Nuestro gabinete mdf estaba casi terminado.

Fig 16: Retoques en la decoración del gabinete mdf.

Fig 17: Gabinete mdf con los coolers atornillados.

Atornillamos los coolers en su lugar, colocamos la estructura con la placa madre, la fuente, los discos en el interior y finalmente las dos piezas de acrílico que entraban en unas ranuras. Este fue el resultado final:

Fig 18: Acabado final del gabinete mdf.

Conclusión

Logramos terminar el gabinete mdf y mi amigo tuvo una cajita para albergar su computadora. Fue algo temporal hasta que finalmente consiguió un gabinete de pc.

A mi parecer las dimensiones estaban bastante exageradas, pero mi amigo estaba preocupado (y con razón) por la refrigeración. En un gabinete de metal el calor se disipa en parte por la conductividad térmica. Además de que proporciona protección a los circuitos frente a ruidos electromagnéticos.

Sin embargo si tenemos en cuenta el hecho de que antes tapaba la computadora con una caja de cartón, el gabinete mdf fue una pequeña mejora.

Salir de la versión móvil
Secured By miniOrange