Introducción

En entradas anterior vimos cómo definir matrices en programación y cargarle datos, en este artículo veremos cómo sumar y restar dos matrices utilizando arreglos bidimensionales.

Para poder sumar o restar dos matrices se necesita que ambas tengan el mismo tamaño, es decir la misma cantidad de filas y la misma cantidad de columnas, de lo contrario no se puede resolver la operación. Esto lo tendremos en cuenta en nuestro algoritmo, al momento de realizar la operación comprobaremos que se cumplan estas condiciones y si todo está en orden se resolverá la suma, caso contrario la matriz devuelta tendrá valor null y este resultado se puede utilizar para hacer comprobaciones posteriores, por ejemplo si la matriz resultante es distinta de null consideramos que la operación fue un éxito.

Datos iniciales

Partiremos de dos matrices A y B con sus datos precargados y analizaremos un método que realizará la suma de dos matrices que se envíen como parámetro y devuelve la matriz resultado, para más detalles sobre métodos en programación, sintaxis y parámetros de entrada y salida consultar este vídeo.

En la figura 1 vemos la declaración de dos matrices de 2×2 y la carga de sus datos, luego en las líneas 28, 29 y 30 se hacen las llamadas a los métodos que se encargarán de sumar, restar o hacer una combinación lineal entre las matrices que se envíen cómo parámetro y devolverán la matriz resultante.

Fig. 1: Declaración de dos matrices A y B, carga de datos y ejecución de los métodos que suman, restan y realizan una combinación lineal de ambas matrices.

Algoritmo para sumar dos matrices en programación, lenguaje C#

Para hacer la suma de dos matrices primero debemos asegurarnos de que ambas matrices tengan el mismo tamaño, si esto es correcto se procede a recorrer cada elemento de las matrices y realizar la suma elemento a elemento, asignando el resultado a una nueva matriz previamente declarada.

Fig. 2: Algoritmo para sumar dos matrices A y B implementado en lenguaje C#, ejemplo en Unity.

Análisis del algoritmo que realiza la suma de dos matrices

Entre las líneas 30 y 56 hemos definido un método que recibe como parámetros dos arreglos bidimensionales («matA» y «matB») y devuelve otro array bidimensional.

En la línea 32 se declara la matriz resultado y se devuelve en la línea 55, al finalizar el procedimiento.

Luego en la línea 34 tenemos una sentencia IF para comprobar si es posible resolver la suma de las matrices, la condición es que las matrices coincidan en cantidad de filas y columnas, si esto es verdadero se procede a resolver la suma, sino se imprime un mensaje en consola indicando que no es posible resolver la operación y a la variable resultado se le asigna null (líneas 51 y 52).

Si se puede resolver la suma de matrices vamos a determinar el número de filas y columnas de la matriz resultado y vamos a crear la estructura de datos para esa matriz, esto lo hacemos en las líneas 36, 37 y 39 respectivamente.

La suma de las matrices se realiza elemento a elemento, por lo tanto usaremos dos bucles anidados, el primero recorrerá cada fila y el bucle del interior recorrerá cada columna, de modo que comenzaremos con una fila y pasaremos por cada una de las columnas, al finalizar pasaremos a la siguiente fila. Dentro del bucle interno simplemente se resuelve la suma de los elementos ij de cada matriz y se lo asigna al elemento ij de la matriz resultado.

Algoritmo para restar dos matrices en programación, lenguaje C#

Para restar dos matrices en programación, el algoritmo es prácticamente igual que el de la suma, solo que cuando se recorren sus elementos, en lugar de sumarlos se los resta, podemos ver una implementación del algoritmo en la figura 3.

Fig. 3: Algoritmo para restar dos matrices A y B implementado en lenguaje C#, ejemplo en Unity.

Algoritmo para realizar la combinación lineal de dos matrices en programación, lenguaje C#

Dado que el algoritmo para sumar y restar matrices es muy similar, se podría definir una función que realice una combinación lineal entre las dos matrices indicadas junto con sus coeficientes, es decir que resuelva la operación:

MatrizC = a * MatrizA + b * MatrizB

De esta forma para realizar la resta de la matriz A menos la matriz B se podría llamar a esta función pasando como parámetro 1 para el coeficiente que acompaña la matriz A y -1 para el coeficiente que acompaña la matriz B, pero además contamos con una función con mayores prestaciones. En la figura 4 vemos el algoritmo que resuelve la combinación lineal entre ambas matrices.

Fig. 4: Algoritmo para resolver la combinación lineal de dos matrices A y B implementado en lenguaje C#, ejemplo en Unity.

Introducción

En entradas anterior vimos cómo definir matrices en programación y cargarle datos, en este artículo veremos cómo multiplicar dos matrices utilizando arreglos bidimensionales.

Datos iniciales

Partiremos de dos matrices A y B con sus datos precargados y analizaremos un método que realizará la suma de dos matrices que se envíen como parámetro y devuelve la matriz resultado, para más detalles sobre métodos en programación, sintaxis y parámetros de entrada y salida consultar este vídeo.

En la figura 1 vemos la declaración de dos matrices de 2×2 y la carga de sus datos, luego en las líneas 27 y 28 se hacen las llamadas al método que se encarga de multiplicar las matrices que se envíen cómo parámetro y devolver la matriz resultante.

Fig. 1: Declaración de dos matrices A y B, carga de datos y ejecución de la función que multiplica dos matrices que se pasen como parámetro.

Algoritmo para multiplicar matrices en programación

El producto de matrices no es conmutativo, de modo que importa el orden en el que se multiplican las matrices. Vamos a considerar que la matriz A está a la izquierda y la multiplicaremos por la matriz B que está a la derecha.

El procedimiento para multiplicar dos matrices consiste en multiplicar cada elemento de la i-ésima fila de la matriz de la izquierda por cada elemento de la j-ésima columna de la matriz derecha y luego sumar estos productos, el resultado de esa suma será el elemento ij de la matriz resultante. Por lo tanto para que se pueda llevar a cabo el producto de matrices debe cumplirse la condición que el número de columnas de la matriz de la izquierda debe coincidir con el número de filas de la matriz de la derecha.

El resultado de la multiplicación de las matrices A y B será otra matriz que tendrá un número de filas igual a las filas de A y un número de columnas igual a la cantidad de columnas de B.

Análisis del algoritmo

En la figura 2 vemos la implementación de un algoritmo que multiplica dos matrices que se envíen como parámetro. Primero se declara la matriz resultado y se leen los tamaños de ambas matrices, esto se hace en las líneas de la 32 a la 36.

Luego se comprueba si es posible multiplicar ambas matrices, de no ser posible el resultado será null, caso contrario se procede a resolver el producto de matrices.

Se crea la estructura de la matriz resultado con la cantidad de filas y columnas correspondientes (ante la duda ver la descripción más arriba). Luego para determinar el valor de cada elemento resultante se utilizan los tres bucles for anidados que se observan en la figura 2.

Fig. 2: Algoritmo que resuelve la multiplicación de dos matrices que se reciben como parámetros y devuelve una matriz que es el resultado del producto de ambas matrices.

Introducción – Circuito y Ley de Ohm

Esta entrada es para mostrar un ejemplo muy simple de aplicación en Unity, se trata de un circuito con una fuente de DC conectada a un resistor.

La fuente DC provee un voltaje constante en el tiempo y el resistor es un componente eléctrico pasivo que se caracteriza por ofrecer resistencia al paso de la corriente eléctrica. Cuando se conectan estos dos componentes como se muestra en la figura 1 se establece una corriente que será proporcional al voltaje e inversamente proporcional a la resistencia, de acuerdo a la Ley de Ohm:

I = V / R

Fig.1: Circuito eléctrico con fuente de DC y un resistor, la corriente que circula se calculará aplicando la Ley de Ohm.

Capturas del programa funcionando

En las figuras 2 y 3 se observa el programa funcionando, el esquema del circuito es una imagen representativa, los tres valores de la derecha son los valores actuales del sistema, el voltaje y la resistencia son parámetros variables mediante los Sliders de la parte inferior y la corriente se calcula como resultado de esos valores.

Fig. 2: Captura del programa funcionando, los sliders permiten modificar los valores de voltaje y de resistencia, la corriente se calcula aplicando la Ley de Ohm.
Fig. 3: Otra captura del programa con otros valores.

Análisis del Script que controla el sistema

A continuación se analiza el Script responsable de controlar el sistema y mostrar la información en pantalla.

Variables del sistema

En la figura 4 se observan las variables definidas en el Script, en las líneas 9, 10 y 11 tenemos unas referencias para los componentes Text que se encargan de mostrar los valores en la interfaz gráfica. Para ver en detalle cómo escribir textos en pantalla de manera dinámica en Unity consultar este vídeo.

Luego se definen tres variables tipo float para el voltaje, la resistencia y la corriente del circuito (líneas 13, 14 y 15).

Fig. 4: Variables del Script que controla el simulador del circuito DC.

Función Start, actualización de valores en pantalla y cálculo de la Ley de Ohm

En la figura 5 hay tres funciones pertenecientes al sistema. Start es una función que Unity ejecuta automáticamente luego de pulsar el botón Play y antes de que se muestre el primer fotograma de la aplicación, dentro de esta función se aplica la Ley de Ohm con los valores iniciales que se definan y se refrescan los valores en pantalla, esto ocurre en la ejecución de los métodos de las líneas 19 y 20 respectivamente. Para más información sobre métodos en programación, parámetros de entrada y salida y sobrecarga paramétrica, consultar este vídeo.

El método «RefreshValues» se encarga de escribir los valores de voltaje, resistencia y corriente en los componentes Text de la interfaz gráfica.

El método «ApplyOhmLaw» realiza el cálculo de la corriente en función del voltaje y la resistencia, aquí se podría tener una indeterminación si la resistencia (el denominador) es nulo, si esto ocurre no se produce un error en tiempo de ejecución, sino que Unity detecta esta situación y la resuelve asignando el valor «Infinity», el cual nos sirve como indicador de que hay una indeterminación.

Fig. 5: Funciones del Script que controla el simulador del circuito DC, inicialización, Ley de Ohm y actualización de interfaz gráfica.

Funciones para modificar valores desde la interfaz gráfica

Para que los Sliders de la interfaz gráfica puedan modificar valores dentro de un Script, deben hacerlo a través de métodos públicos, los métodos que se observan en la figura 6 cumplen esta función, el de arriba permite modificar el valor de voltaje y el de abajo el valor de resistencia. Notar que no solo se modifican los valores sino que se vuelve a aplicar la Ley de Ohm y se refrescan los valores en pantalla.

Fig. 6: Funciones del Script que controla el simulador del circuito DC, métodos públicos que permiten modificar los valores de voltaje y resistencia al manipular los Sliders de la interfaz gráfica.

Introducción

En este artículo vamos a ver algunas alternativas para hacer indicadores lumínicos en Unity para que una máquina pueda reflejar su estado de encendido o el estado de alguna de sus variables booleanas.

Supongamos que tenemos un Script que modela el comportamiento de una máquina cualquiera. Esa máquina tiene estados que nos interesa representar en pantalla, por ejemplo si la máquina está encendida o apagada mediante un indicador LED.

Vamos a resolver esta situación de la siguiente manera, crearemos un Script que se encargue de controlar el estado del indicador luminoso y este Script tendrá un conjunto de funciones que se podrán llamar para cambiar el estado del indiciador. De esta forma logramos que la máquina no se encargue directamente de alterar el estado del indicador luminoso sino que le dice al Script de control en qué estado debe mostrarlo y luego el Script del indicador se encargará de resolver esa tarea dependiendo el tipo de indicador que implementemos.

Fig. 1: Esquema de Scripts de control de una máquina genérica y un indicador luminoso.

Propongo esta forma de resolver el problema ya que existen muchas alternativas para hacer un indicador luminoso en Unity, entonces sería conveniente delegar ese comportamiento a otro Script en donde se ataque el problema en detalle, mientras que el aparato que debe usar el indicador luminoso simplemente ejecuta las funciones apropiadas. Esto nos facilita hacer mejoras al sistema o dotarlo de mayores prestaciones.

Alternativas para hacer un indicador luminoso en Unity

A continuación voy a mencionar algunas formas en las que se puede hacer un indicador luminoso en Unity.

Indicador con componente Light

Una alternativa es utilizar un GameObject que tenga asignado un componente Light, con esto obtendríamos una luz que interactúa con los modelos 3D de la escena. Para controlar la luz podríamos habilitar y deshabilitar el componente Light, o habilitar y deshabilitar el GameObject directamente. Sino también podemos modificar el color de la emisión de luz, por ejemplo verde para encendido y rojo para apagado.

Otra posibilidad es tener un GameObject con un componente Light para el estado de encendido y otro GameObject con componente Light para el estado apagado. Al momento de setear el estadodel indicador, apagamos un GameObject y encendemos el otro según corresponda.

Indicador con objeto 3D con material emisivo

Si contamos con un modelo 3D de una lámpara, podemos asignarle un material con emisividad, esto influye más que nada en la apareciencia del objeto, ya que estos materiales no suelen emitir luz que interactúa con los demás objetos. Sin embargo se puede combinar esta técnica con la anterior mensionada y así conseguir un resultado más interesante.

Indicador luminoso con Sprite

Es posible establecer un conjunto de Sprites que cumplan la función de indicador lumínico, un Sprite es una imagen, usualmente de fondo transparente, que se utiliza para renderizar objetos en 2D a través del componente Sprite Renderer .Como se ha mencionado anteriormente, podríamos tener un Sprite para la luz encendida y otro para la luz apagada, o podríamos cambiar el color de la luz según el estado.

Indicador con imagen en Canvas de la interfaz de usuario

Además se pueden incluir estos indicadores en la interfaz gráfica, en este caso se necesita configurar un Canvas y luego el indicador será un objeto que cuente con componente Image y que se encuentre dentro de la jerarquía del Canvas. Este caso es similar al de los Sprites, necesitaremos una imagen para la luz encendida y otra para la luz apagada, o utilizar el mismo Sprite pero modificar su color según el estado.

Ejemplo de indicador luminoso hecho en Unity C# (con descarga)

A continuación vamos a ver un ejemplo de implementación de un indicadir luminoso hecho en Unity, además haciendo clic en el siguiente enlace puedes descargar el paquete de Unity donde están los scripts y la escena ya montada, para que puedas importarlo y probarlo.

Descargar paquete de Unity – Indicador Luminoso C#

La jerarquía de la escena se ilustra en la figura 2, tenemos los objetos «Machine» e «IndicatorLight» que tienen asignado los Scripts «Machine» e «IndicatorLight» respectivamente, estos Scripts están basados en el esquema de la figura 1. El objeto GreenLight tiene un componente Light y es el que va a controlar el Script Indicator Light. Además tenemos dos botones en el Canvas que nos permitirán encender y apagar la máquina.

Fig. 2: Jerarquía de la escena dentro del paquete de descarga.

En las figuras 3 y 4 vemos el inspector de los GameObjects Machine e IndicatorLight. Noten como en el inspector del Script Machine hay una referencia asignada para un objeto tipo IndicatorLight, esto quiere decir que Machine podrá acceder a las funciones públicas que definamos dentro de Indicator Light. También observen cómo en el inspector del Script IndicatorLight hay una referencia para un componente Light, esto quiere decir que IndicatorLight podrá efectuar modificaciones sobre el componente Light, por ejemplo encenderlo y apagarlo, cambiar el color, intensidad, etc.

Fig. 3: GameObject Machine con el Script Machine asignado.
Fig. 4: GameObject IndicatorLight con el Script IndicatorLight asignado.

En la figura 5 vemos el inspector de uno de los botones del Canvas, noten como en el evento OnClick se ejecuta una función dentro de Machine. Para saber más sobre cómo usar botones en Unity, consultar este vídeo o este artículo.

Fig. 5: Inspector de uno de los botones del Canvas que permiten encender y apagar la máquina.

Script del indicador lumínico

En la figura 6 vemos una captura del Script que controla el indicador, noten que solamente tiene una referencia para un componente Light llamada «myLight» (asignada en el inspector en la figura 4) y luego cuenta con una función pública que permite setear el estado del indicador, dicha función recibe como parámetro una variable booleana que dicta el estado a setear y dentro de la función lo que se hace es asignar el parámetro de la función al estado de activación del componente Light, quiere decir que si ejecutamos el método «SetIndicatorState(true)» la lámpara se encenderá y con SetIndicatorState(false) la lámpara se apagará. Aquí un vídeo completo sobre métodos en programación.

Fig. 6: Script que modela el comportamiento de un indicador lumínico en Unity.

Script de la máquina que ocupa el indicador luminoso

En la figura 7 vemos el Script de la máquina, se tienen tres variables definidas en las líneas 5, 6 y 7, «machineInitialState» indica el estado inicial de la máquina cuando le demos a Play y se puede setear desde el inspector, como se observa en la figura 3. La variable IndicatorLight es una referencia para un objeto basado en el Script anterior, noten que este Script está asignado al GameObject con el mismo nombre «IndicatorLight», de modo que en el inspector de Machine, arrastramos el objeto que contiene el Script IndicatorLight para asignar la referencia. Referencias en programación es un tema muy importante, entenderlo nos permitirá dar con cualquier objeto que necesitemos modificar o utilizar en nuestros Scripts, para ello hice esta serie de vídeos.

El Script Machine además cuenta con una variable booleana pivada que indica el estado de activación de la máquina, la variable «machineActive», noten que en el método Start hacemos que esta variable sea igual al estado inicial y luego ejecutamos la función «SetMachineState» pasandocomo parámetro el propio estado de activación de la máquina.

La función «SetMachineState» está definida como pública, por lo que se puede ejecutar desde contextos externos al propio Script (por ejemplo desde los botones de la interfaz gráfica). Esta función lo que hace es setear el estado de activación de la máquina con el valor que se envía como parámetro (línea 17) y luego ejecuta la función pública del indicador lumínico pasando como parámetro el estado de activación de la máquina.

Fig. 7: Script en Unity que modela el comportamiento básico de una máquina que se puede encender y apagar y cuenta con un indicador lumínico.

Introducción

Si bien podríamos crear nuestra propia estructura de datos para representar vectores en el plano y el espacio en Unity, dentro de sus librerías ya vienen definidas dos herramientas para este propósito. Se trata de las clases Vector2 y Vector3, que consisten en vectores de dos y tres dimensiones respectivamente, además cuentan con un conjunto de funciones que nos permite resolver todas las operaciones algebráicas entre vectores, como el producto punto, producto cruz, cálculos de norma, etc.

En el siguiente vídeo puedes ver una introducción a los vectores para el plano y el espacio en Unity.

Declaración del tipo de dato Vector2 y Vector3

Comencemos viendo cómo declarar estos datos en un Script para poder utilizarlos.

La declaración de la variable consiste en indicar la visibilidad, luego el tipo de dato, el nombre de referencia y el valor inicial, la visibilidad podemos omitirla y por defecto tendrá visibilidad privada, el valor inicial también se puede omitir e iniciarlo más adelante, en este caso los vectores tendrán el valor por defecto que será el vector nulo de ambos espacios. En la figura 1 vemos algunos ejemplos de cómo declarar los vectores en los Scripts.

Fig. 1: Algunos ejemplos de declaración de vectores en el plano y en el espacio en Unity.

Cuando asignemos el Script a un GameObject, en el inspector podremos ver algunas de las variables dependiendo de la visibilidad que se haya indicado. La visibilidad pública permite que la variable aparezca en el inspector y la privada puede aparecer si se añade el modificador SerializeField, como se observa en la figura 1 constrastada con la figura 2.

Fig. 2: Visualización en el inspector de Unity de los vectores declarados en la figura anterior.

Operaciones de suma y resta entre vectores en el plano y en el espacio en Unity

A continuación vamos a declarar un conjunto de vectores y hacer las operaciones básicas de suma y resta entre vectores en el plano y en el espacio.

Al sumar o restar dos vectores en R2 el resultado será un tercer vector que también pertenecerá al espacio R2, lo mismo para los vectores en R3. Por lo tanto vamos a definir dos vectores de cada clase que serán los sumandos y luego dos vectores más del mismo tipo en donde almacenaremos los resultados de la suma y resta.

Los sumandos serán los vectores A y B, la suma la almacenaremos en el vector C y la resta en el vector D.

En la figura 3 vemos en ejemplo en el que se declaran los vectores y luego en el método Start se ingresan los valores para estos vectores y luego se resuelven las operaciones de suma y resta.

Fig. 3: Declaración de vectores en el plano y el espacio, inicialización y operaciones de suma y resta.

El método Start es un mensaje de Unity, por lo tanto para que Unity lo ejecute automáticamente debemos asignar al Script a un GameObject de la escena que esté habilitado y luego entrar en el modo juego (botón Play de la figura 4).

Fig. 4: Para que los Scripts asignados en la escena se ejecuten hay que entrar en el modo juego.

En la figura 5 vemos el resultado de la ejecución del Script de la figura 3.

Fig. 5: Resultado de la ejecución del código que se muestra en la figura 3.

Introducción

En este artículo vamos a ver las operaciones de producto de un vector por un escalar, el producto escalar entre vectores (producto punto) y el producto vectorial entre vectores (producto cruz).

Para ver cómo declarar vectores en el plano y el espacio y realizar las operaciones de suma y resta, consultar el artículo anterior.

Operaciones producto con Vectores en el plano en Unity

En la figura número 1 vemos la declaración de vectores y variables reales para resolver las operaciones entre vectores en el plano en Unity.

Dentro de la función Start se inicializan los valores y se calculan distintos productos.

Fig. 1: Declaración e inicialización de vectores y distintas operaciones con vectores en el plano en Unity.

Producto de un escalar por un Vector en el plano en Unity

Para resolver este producto necesitamos una variable para el escalar y otra variable para el vector en R2, en las líneas 7 y 9 de la figura 1 respectivamente se declaran estas variables, en las líneas 18 y 19 se hace la inicialización de estas variables y en la línea 22 se resuelve el producto del escalar por el vector, observen que este producto se resuelva directamente con el operador *.

El resultado del producto de un escalar por un vector en R2 es un vector que pertenece a R2, por lo tanto el resultado de la operación se almacena en una variable tipo Vector2.

Producto escalar entre dos vectores en el plano en Unity (producto punto)

Para resolver el producto punto entre dos vectores de R2 en Unity se recurre a una función llamada Dot que está definida dentro de la clase Vector2, para ello se utiliza el nombre de la clase (Vector2), luego un punto para acceder a sus miembros y funciones internas y llamamos a la función Dot, entre paréntesis debemos enviar los vectores que se se quiere multiplicar, esto se resuelve en la línea 23 de la figura 1.

El resultado del producto punto entre dos vectores es un valor real, por lo que ese resultado se almacena en una variable tipo float.

Fig. 2: Resultado de la ejecución de las instrucciones definidas en el Script de la figura 1.

En la figura 2 vemos el resultado de la ejecución del Script de la figura 1, recordamos que para que se ejecute un Script es necesario asignarlo a un GameObject de la escena que esté habilitado y luego entrar en el modo juego.

Operaciones producto con Vectores en el espacio en Unity

En la figura 3 vemos la declaración de vectores y variables reales para resolver las operaciones entre vectores en el espacio en Unity.

Dentro de la función Start se inicializan los valores y se calculan los distintos productos.

Fig. 3: Declaración e inicialización de vectores y distintas operaciones con vectores en el espacio en Unity.

Producto de un escalar por un Vector en el espacio en Unity

Para resolver este producto necesitamos una variable para el escalar y otra variable para el vector en R3, en las líneas 7 y 9 de la figura 3 respectivamente se declaran estas variables, en las líneas 19 y 20 se hace la inicialización de estas variables y en la línea 23 se resuelve el producto del escalar por el vector, observen que este producto se resuelva directamente con el operador *.

El resultado del producto de un escalar por un vector en R3 es un vector que pertenece a R3, por lo tanto el resultado de la operación se almacena en una variable tipo Vector3.

Producto escalar entre dos vectores en el espacio en Unity (producto punto)

Para resolver el producto punto entre dos vectores de R3 en Unity se recurre a una función llamada Dot que está definida dentro de la clase Vector3, para ello se utiliza el nombre de la clase (Vector3), luego un punto para acceder a sus miembros y funciones internas y llamamos a la función Dot, entre paréntesis debemos enviar los vectores que se se quiere multiplicar, esto se resuelve en la línea 24 de la figura 3.

El resultado del producto punto entre dos vectores es un valor real, por lo que ese resultado se almacena en una variable tipo float.

Producto vectorial entre dos vectores en el espacio en Unity (producto cruz)

Para resolver el producto cruz entre dos vectores de R3 en Unity se recurre a una función llamada Cross que está definida dentro de la clase Vector3, para ello se utiliza el nombre de la clase (Vector3), luego un punto para acceder a sus miembros y funciones internas y llamamos a la función Cross, entre paréntesis debemos enviar los vectores que se se quiere multiplicar, esto se resuelve en la línea 25 de la figura 3.

El resultado del producto cruz entre dos vectores es un vector que pertenece a R3, por lo que ese resultado se almacena en una variable tipo Vector3.

Fig. 4: Resultado de la ejecución de las instrucciones definidas en el Script de la figura 3.

En la figura 4 vemos el resultado de la ejecución del Script de la figura 3.

Introducción

En este artículo voy a mostrar una forma en la que se puede representar la evolución de magnitudes que dependen del tiempo en Unity.

Supongamos que tenemos una ecuación cualquiera, por ejemplo Y = X2 la cual consiste una parábola con vértice en (0,0) y sus ramas en el semiplano superior. Considerando la variable X como el tiempo, en este artículo vamos a analizar cómo hacer en Unity para que la variable Y describa la parábola a medida que el tiempo avanza.

Descargar paquete de Unity con el ejemplo que se analiza en este artículo

Punto de partida

Consideremos la gráfica de la función y = t2 para un tiempo cualquiera t i mayor a 0, en la figura 2 vemos el punto de la gráfica cuya coordenada es (t i ,y i)

Fig. 1: Gráfica de la función y = t2.

Fig. 2: Coordenada del punto (t i ,y i).

Ahora consideremos que dejamos pasar un cierto lapso de tiempo Δt, ahora nos encontramos en el tiempo t i+1 que no es otra cosa que el tiempo anterior mas el valor Δt, es decir que t i+1 = t i + Δt . En la figura 3 se considera el valor de la función en el tiempo t i+1, este se reemplaza por el valor t i + Δt y se hace el desarrollo matemático hasta llegar a las expresión de y i+1 (la función en t i+1) pero desde la perspectiva de un tiempo anterior.

Fig. 3: Expresión de la valor de Y en el tiempo ( t i + Δt).

Al final de la figura 3 llegamos a una expresión para el siguiente valor de la función en términos del valor actual y del incremento de tiempo, en otras palabras en la figura 4 vemos que si al valor actual le agregamos un cierto incremento dentro del cual se encuentra el propio valor actual y el incremento de tiempo, esta suma nos devuelve el siguiente valor.

Fig. 4: Expresión del siguiente valor de Y en función del valor anterior y un incremento Δt.

La expresión anterior nos permite realizar un proceso iterativo, es decir repitiendo el cálculo de la ecuación y actualizando los valores vamos a lograr recorrer cada punto de la parábola para t > 0, para incluir los valores en los que t<0 habría que analizar qué ocurre con ese valor absoluto, por simplicidad decidí omitirlo y quedarme solo con la rama de la derecha de la parábola.

Implementación en Unity

Ahora vamos a ver cómo se pueden aplicar en Unity esta ecuación y hacer que el sistema evolucione en el tiempo.

El concepto matemático Δt en Unity

En matemática se utiliza la letra griega delta (Δ) delante de una variable para indicar un incremento en dicha variable, por ejemplo Δt puede representar t2 – t1, siendo t1 y t2 dos momentos distintos en el tiempo. En Unity suele ser de interes conocer esta magnitud, ya sea el Δt que existe entre dos frames de la aplicación o el Δt entre dos actualizaciones físicas del motor, estas magnitudes son necesarias para producir cambios coherentes en el tiempo, no solo nos permite controlar de manera exacta la magnitud de los cambios, es decir por ejemplo alterar una variable en tantas unidades por segundo, sino que garantizan que estos cambios sean iguales para todos los ordenadores, sin importar la capacidad del hardware.

Para comprender un poco mejor esta parte voy a dejar dos vídeos pertenecientes a mi canal sobre desarrollo de aplicaciones en Unity, uno se trata sobre cómo incrementar gradualmente variables en el tiempo, que puede ayudar a comprender el significado del delta time en Unity, el otro se trata de un experimento para analizar las diferencias entre las funciones Update y FixedUpdate.

Contenido del Script

En la figura 6 se muestran las variables utilizadas y la función Start que se ejecuta en el momento inicial en el que Unity entra en el modo ejecución. Se definen dos variables tipo float, es decir para almacenar números con decimales, una para la variable «t» y otra para la variable «y», además se definen dos listas de floats para ir almacenando los valores registrados en la evolución del sistema. Estas listas se crean en la función Start.

Fig. 5: Variables definidas en el Script dentro del paquete de descarga

Las instrucciones que se encargarán de la evolución del sistema en el tiempo están definidas dentro de la función Update, una función que Unity ejecuta de manera automática una vez por cada frame que se produce. En la figura 6 vemos una expresión matemática relevada de la figura 4 en la que se muestra el incremento que hay que hacerle a la variable y para pasar al siguiente valor dado por el incremento del delta de tiempo. A la derecha, en la figura 7 vemos las instrucciones escritas en Unity que realizan esta misma tarea.

Fig. 6: Incremento sobre la variable Y en función del valor anterior y un incremento Δt, relevada de la ecuación de la figura 4.
Fig. 7: Contenido de la función Update que determina la evolución del sistema en el tiempo.

La variable t se va incrementando con el valor de deltaTime, que en el video enlazado mas arriba se muestra que es un valor variable y depende de la potencia del ordenador, en la variable Y se va haciendo un incremento de acuerdo a la expresión de la figura 6.

Las instrucciones en las líneas 27 y 28 añaden los nuevos valores de t y de y a las listas simplemente para registrarlos y poder visualizarlos en el inspector en Unity. La instrucción de la figura 30 es la modificiación de la posición del GameObject al cual ese Script está asignado de acuerdo a los valores calculados.

Prueba de ejecución

Al entrar en el modo Play comienzan a registrarse los valores en las listas de T y de Y y además la esfera que tiene asignado el Script se mueve en una trayectoria parabólica, como recorriendo la rama de la derecha de la parábola de la figura 1.

Fig. 8: Valores registrado al entrar en el modo Play.

Conclusión

El propósito de este artículo era mostrar una forma de implementar una ecuación matemática o el análisis de la misma a Unity y lograr que una variable evolucione en el tiempo de acuerdo a esta ecuación. Entender esto puede ayudarnos a crear sistemas mas complejos que estén basados en matemática y con resultados tan precisos como lo permitan los tipos de datos utilizados.

Introducción

Herencia y polimorfismo son dos conceptos muy importantes en la programación orientada a objetos (POO de ahora en más) y saber cómo implementarlos nos puede potenciar nuestra capacidad para resolver problemas.

El ejemplo que vamos a resolver en este artículo es definir una jerarquía de clases en la que se tienen las siguientes clases: Device, Mouse y Teclado, las clases Mouse y Teclado tendrán sus propias propiedades y heredarán de la clase Device, como se ilustra en el diagrama de clases de la figura 1.

Tendremos funciones para crear nuevos Mouse y Teclados que se añadirán a una lista y tendremos definiremos la función ToString() en las tres clases, de tal forma que esta función devolverá un resultado dependiendo de la naturaleza del objeto al que se ejecute esta función.

En el siguiente vídeo se explica la implementación del ejercicio propuesto de herencia y polimorfismo en lenguaje C# para Unity.

Clic aquí para descargar el paquete de Unity con el ejercicio de herencia y polimorfismo

Repaso de los conceptos clave antes de pasar al ejemplo

Concepto de clases en programación

Las clases en programación son una estructura de datos que nos permiten modelar objetos con propiedades y funciones asociadas. Se utilizan como plantilla para crear instancias de estos objetos, cada una de estas instancias tiene la misma estructura (propiedades y funciones), pero tienen un estado individual, es decir que los valores almacenados en sus campos son propios de cada instancia.

Concepto de herencia en programación

Para resolver problemas utilizando POO lo que se hace es crear clases de programación que modelan un determinado objeto. La herencia nos permite crear clases que serán hijas de otras clases (super clases), esto permite reutilizar el código y aumentar la abstracción ya que las propiedades y funciones definidas en la clase de mayor jerarquía se heredarán a las clases de menor jerarquía, dando la posibilidad de utilizar esas funciones tal cual están, extender su comportamiento o reescribirlas completamente.

Una ventaja de esto es que podemos agrupar objetos de distinto tipo en colecciones (por ejemplo arrays) que sean del tipo de la super clase que tengan en común.

Concepto de polimorfismo en programación

El polimorfismo es la capacidad de los objetos de responder a un mismo mensaje (llamada a una función) de distintas formas.

Por ejemplo, supongamos que tenemos tres clases de programación, Persona, Trabajador y Estudiante, Trabajador y Estudiante heredan de la clase Persona, el razonamiento de esto es que los Trabajadores y Estudiantes son personas, solo que un grupo específico de ellas.

Ahora supongamos que dentro de estas tres clases hay una función llamada ´Introducirse´ que devuelve un string, al ejecutarlo sobre una persona el mensaje devuelto es: «´Mi nombre es´ + variableNombre». Si la función se ejecuta sobre un Trabajador, el mensaje es el mismo que el de una persona pero además se le añade: «´, soy un trabajador.». Si en cambio ejecutamos la función sobre un estudiante, al mensaje de Persona se le añadirá: «´, soy un estudiante.´».

Entonces tenemos una estructura de clases en la que los objetos de los tres tipos responden al mismo mensaje, pero el resultado de esa ejecución será distinto dependiendo el tipo de objeto del que se trate.

Ejemplo de herencia y polimorfismo en programación implementado en C#, Unity

Vamos a ver cómo implementar el sistema propuesto en la introducción de este artículo, se trata de una estructura de clases como la que se ilustra en la figura 1, la clase Device tiene un dato privado de tipo String llamado «identificationCode» y una función pública llamada «ToString()». Las clases Mouse y Keyboard heredan de la clase Device (lo cual tiene sentido ya que los mouse y teclados son en efecto dispositivos), estas clases tienen sus propios datos, Mouse tiene una variable privada tipo float llamada «dpi», que representan los DPI del mouse en cuestión y teclado tiene una variable privada entera llamada «numberOfKeys», que representa la cantidad de teclas que tiene el teclado en cuestión. Ambas clases responden al mensaje ToString().

Fig. 1: Ejemplo de un diagrama de clase basado en la explicación del párrafo anterior.

Implementación de la clase Device (super clase)

En la figura 2 está la implementación de la clase Device en lenguaje C# en Unity, esta es la clase de la que van a heredar las clases Mousue y Device, noten que en la definición de la clase (línea 5 de la figura 2) se ve que la clase Device hereda de Object, la cual es la clase base que da origen a todas las demás clases.

En la línea 7 se declara el string «identificationCode» y en la línea 15 la función pública «ToString()», observen que está declarada como «virtual», con esto indicamos que la función «ToString» podrá ser sobreescrita por las subclases de Device. En la línea 10 está el constructor de la clase, una función que permite crear un objeto tipo Device, el constructor requiere como parámetro que se le pase el string con el que se iniciará la variable «identificationCode».

Fig. 2: Implementación de la clase Device según diagrama de la figura 1.

Implementación de las clases Mouse y Keyboard (sub clases)

En las figuras 3 y 4 vemos los Scripts en los que se definen las clases Mouse y Keyboard, subclases de la clase Device, observen que cada una tiene sus propios parámetros, DPI para la clase Mouse y número de teclas para la clase Keyboard. Cada una tiene su propio constructor que permite crear los objetos (líneas 9 de ambas figuras 3 y 4), observen que requieren como parámetro el string de identificación para la clase Device y en el caso de Mouse se piden los DPI, mientras que en el caso de Keyboard, además del ID se pide el número de teclas. En los constructores se hace la llamada al constructor de la subclase, la parte «: base(id)».

Luego tenemos las funciones «ToString()» (líneas 14 de ambas figuras 3 y 4), en la definición de la función se incluye la palabra «override» que indica que se va a sobreescribir la función ToString() de Device, pero esto no significa que se pierde el contenido de la función ToString() presente en Device. Esta puede ser reutilizado haciendo referencia a la superclase con la palabra «base», como se observa en las líneas 16 de ambas figuras, se ejecuta el método ToString() de la clase base (Device) y esto da como resultado lo que se muestra en la línea 17 de la figura 2; a esto se le adiciona otro mensaje que va a depender de la clase, Mouse responde con una frase y Keyboard responde con otra, aquí es donde se ilustra el concepto de polimorfismo en programación, una misma función que produce distintos resultados dependiendo del objeto del que se trate.

Fig. 3: Implementación de la clase Mouse según diagrama de la figura 1.
Fig. 4: Implementación de la clase Keyboard según diagrama de la figura 1.

Ejemplo de un programa que crea y gestiona los objetos

La jerarquía de clases nos permite crear objetos de distintos tipos que tienen una super clase en común, la idea es que esos objetos los vamos a usar para resolver algún problema, por lo que se parte del problema y luego se propone una estructura de clases que facilitará la resolución del problema. En este caso vamos a tener un Script que será el administrador de los dispositivos en el que se podrá crear nuevos dispositivos, concretamente Mouse o Teclados y las referencias de esos objetos estarán almacenadas en una lista de dispositivos, el administrador tendrá funciones para listar los dispositivos o limpiar la lista. Esto es solo un ejemplo de cómo hacer uso de la estructura de clases propuestas.

Funciones para crear nuevos objetos

En la figura 5 se observan las funciones que permiten crear nuevos dispositivos, AddMouse() y AddKeyboard(), estas funciones las ejecutarán un conjunto de botones en la interfaz gráfica (consultar figura 7).

La función AddMouse genera datos aleatorios y luego crea un nuevo objeto tipo Mouse llamando al constructor de la clase Mouse (línea 34 de la figura 5), finalmente ejecuta el método AddDevice pasando como parámetro el Mouse creado. Análogamente la función AddKeyboard genera datos, crea el objeto y ejecuta AddDevice pasando como parámetro el objeto creado.

La función AddDevice recibe como parámetro un objeto de tipo Device y ya que, tanto Mouse como Keyboards son en esencia Devices (por la jerarquía de clases), es posible pasar estos objetos como parámetros por más que no sean específicamente objetos tipo Device. Dentro de la función no sabemos si el objeto que viene como parámetro es un Mouse o un Keyboard pero eso no importa, lo tratamos como un Device, se añade a la lista y se actualiza la información de la interfaz gráfica. Observen como en la línea 57 de la figura 5 se ejecuta la función ToString() sobre el device, esta función es polimórfica, si el Device es un Mouse se ejecuta el ToString de Mouse y si es un Keyboard se ejecuta el ToString de Keyboard.

Fig. 5: Funciones de creación de nuevos objetos Mouse y Keyboard.

Funciones para utilizar los objetos

Como un ejemplo de utilización de los objetos creados hice una función que permite listar todos los objetos, es decir generar un texto a partir de ejecutar la función polimórfica ToString de cada uno de los objetos de la colección, este texto se muestra en el panel de la figura 7. Esta función se ilustra en la figura 6, como se observa se genera rellena una variable tipo string llamada «list» con el resultado de la ejecución de la función ToString() sobre cada uno de los elementos presentes en la lista de dispositivos y luego ese texto se imprime en la interfaz gráfica. En este punto se evidencia el polimorfismo ya que ToString() es una función polimórfica cuyo resultado dependerá si el objeto dispositivo de la lista es un Mouse o un Teclado.

Fig. 6: Función para listar todos los objetos Device que se encuentran en la lista.

Fig. 7: Captura del programa implementado en Unity que permite crear nuevos objetos Mouse y Keyboard y listarlos.

Esta funcionalidad no solo te permite ver los pasos en pantalla, sino también los disparos enemigos y cofres cercanos a tu posición. Además, al activar esta opción, podrás detectar mejor a los enemigos y conocer con mayor facilidad su ubicación.

Procedimiento paso a paso para mostrar pasos en pantalla en Fortnite

Paso 1: Cuando estés en el lobby de Fortnite, deberás acceder al menú del juego que aparecerá en forma de icono en la parte superior derecha de la pantalla.

Paso 2: Una vez que ya hayas accedido al menú, deberás ir hacia la parte de abajo del mismo, hasta encontrar el engranaje. Así podrás entrar a la configuración.

Paso 3: Luego de realizar los pasos anteriores, estarás en la configuración. Allí deberás encontrar el apartado de «sonido», que se sitúa en la parte superior de la pantalla, justo al lado de «video».

Paso 4: En el apartado de «sonido», baja hasta encontrar la opción «visualizar efectos de sonido» y actívala.

Diana's Magical Journey

JAM: Ludum Dare 54

Sinópsis

Diana es una aspirante a sanadora, quien ha pasado su vida en comunión con la naturaleza, aprendiendo de ella.
Un buen día una oportunidad para realizar su sueño toca a la puerta.

Cómo jugar

Muévete con las FLECHAS, recoge libros con ESPACIO, acércate al mostrador para ATENDER CLIENTES y ENTREGAR LIBROS.

Desarrollado para la Ludum Dare 54 (2023) con el tema "Limited Space"
por:

Cristina Fernández Cantizano

GAME DESIGNER

Álvaro Ruz
Gallardo

PIXEL ARTIST

Martín Heintz
Thomas

MUSIC

Hernán
Bravo

UNITY DEVELOPER

Lista de compilaciones en orden cronológico

Este juego fue desarrollado para la Patagonia Game Jam edición 2023, participamos de manera virtual junto a Martín y Steph.

Hay 16 compilaciones que se pueden testear

BRAIN UP

Sinópsis

Tras pasar una eternidad en una ilusión que detiene el tiempo se te concede una oportunidad para liberar tu mente.

Cómo jugar

Controla el personaje con las teclas WASD, apunta y dispara con el Mouse. REINICIA PULSANDO R.

Bugs que hay actualmente

  • A veces los textos desaparecen al activar la pantalla completa. 
  • Los cerebros spawnean en una esquina en el pasillo por un frame y luego se transportan a su posición de spawn (o no)

Desarrollado para la Patagonia Game Jam 2023 con el tema "Inteligencia artificial"
por:

Martín Heintz Thomas

MÚSICA Y SFX

Steph Stange

UI/UX

Hernán Bravo

UNITY & BLENDER DEVELOPER

Brain Up en YouTube

GAMEPLAY - Canal Ventousa Wins

Brain Up adaptado a VR con Oculus Quest 2

Lista de compilaciones en orden cronológico

Este juego fue desarrollado para la Patagonia Game Jam edición 2023, participamos de manera virtual junto a Martín y Steph.

Hay 16 compilaciones que se pueden testear

En este vídeo vemos qué formato utilizar para exportar de Blender a Unity y cómo hacerlo, junto a otros detalles como por ejemplo crear nuevos materiales en Unity, configurarle las texturas y asignar esos materiales al modelo 3D en Unity, sobreescribiendo el material que viene definido en Blender.

Introducción

Existen varios formatos para exportar modelos 3D en Blender que son compatibles con Unity, recomiendo que uses uno de los dos siguientes, el formato .FBX o directamente el archivo de Blender en formato .Blend.



Antes de avanzar dejo un vídeo mostrando cómo exportar modelo 3D en FORMATO FBX de Blender a Unity

En el siguiente vídeo no vemos qué formato utilizar para exportar de Blender a Unity y cómo hacerlo, sino también otros detalles como por ejemplo crear nuevos materiales en Unity, configurarle las texturas y asignar esos materiales al modelo 3D en Unity, sobreescribiendo el material que viene definido en Blender.

ARTÍCULO DETALLADO SOBRE CÓMO EXPORTAR MODELOS DE BLENDER A UNITY

Exportar en formato FBX

Si usas el formato FBX para exportar tus modelos de Blender a Unity, dentro del archivo se empaquetarán varias cosas además de los modelos 3D. Algunas de ellas son las siguientes:

  • La estructura jerárquica definida en el Outliner se exportará prácticamente igual o muy similar y veremos esa estructura jerárquica en los GameObjects de la jerarquía en Unity.
  • Los nombres de los objetos en Blender se usarán también en Unity.
  • Los materiales definidos dentro de un modelo 3D en Blender estarán presentes dentro del archivo importado en Unity y estarán aplicados al modelo 3D, solo que en principio se encuentran bloqueados (ver figura 1), no se pueden editar, para hacerlo hay que extraerlos del archivo FBX.
  • El color base elegido en el material será el mismo que se aplique en el material en Unity. Esto para el shader Principled BSDF.
  • Las texturas conectadas al color base y a la entrada de normales estarán presentes en Unity siempre que los archivos de las texturas estén presentes al momento de importar el archivo en formato FBX en Unity. Esas texturas estarán conectadas en el mapa de Albedo y Normales en Unity.
  • Las animaciones hechas en con Dope Sheet y clips Nonlinear Animation se incluiran dentro del archivo en formato FBX.
  • Objetos como luces y cámaras en Blender se exportarán como luces y cámaras en Unity.
Fig. 1: Material definido en Blender se encuentra bloqueado en Unity. Para usarlo hay que extraerlo del archivo.

Desventajas de usar el formato FBX

Una de las principales desventajas es actualizar el modelo exportado cuando se hacen cambios en Blender. Lo que hago es reemplazar el archivo que se encuentra en la carpeta de Unity con el nuevo archivo exportado de Blender. VER EL PROCEDIMIENTO PARA ACTUALIZAR CAMBIOS EN EL MODELO EN EL VÍDEO DE ARRIBA.

Utilizar el archivo de edición de Blender (Blend File)

Puedes usar el archivo de Blender directamente en Unity y tendrás acceso a la mayoría de los elementos de la lista anterior correspondiente al formato FBX.

En casos puntuales podrían surgir problemas, por ejemplo cuando se hacen cambios en la versión de Blender o Unity, me ha ocurrido que el archivo de Blender no se podía utilizar, pero luego en siguientes actualizaciones el problema se solucionó.

Fig. 2: Archivo de Blender dentro de una carpeta de un proyecto en el motor Unity. El archivo puede abrirse haciendo doble clic y arrastrarlo a la escena para usarlo.


Ventajas de usar el archivo .Blend directamente en Unity

Para mi la principal ventaja de usar directamente el archivo de Blender en Unity es la comodidad y la facilidad para hacer cambios en el modelo. Aquí tengo un clip en el que muestro cómo trabajar con los archivos .Blend en Unity. Con este método podemos abrir el archivo directamente al darle doble clic en Unity, editarlo, guardarlo y luego en Unity los cambios se actualizan automáticamente.

Desventajas de usar el archivo .Blend directamente en Unity

Una de las desventajas más importantes de trabajar directamente con el archivo de Blender en Unity son los tiempos de carga, puede que sientas que Unity funcione más lento, ya que toma un tiempo procesar estos archivos, cuando los agregamos a la escena y cuando los modificamos, puede que sea algo bastante molesto dependiendo de las capacidades de tu computadora. Aunque si lo pensamos por un momento ese tiempo de espera puede que no sea tan grande como el tiempo que tardas en volver a exportar en formato FBX, reemplazar el archivo y aún así esperar el tiempo de procesamiento que Unity le dedique a esa tarea.

Otra desventaja importante es el hecho de trabajar con animaciones, aún no he encontrado una buena forma de trabajar en Unity con un archivo .Blend con varios clips de animación hechos en Blender.

Una desventaja, quizás no tan importante dadas las capacidades de los dispositivos actuales, es que el archivo .Blend es más pesado que el archivo FBX y además Blender realiza una copia de seguridad para cada archivo, por lo que el peso total es aún más grande.

Qué son los COMPONENTES en Unity y para qué se utilizan

Una COMPONENTE en Unity es un conjunto de datos y funciones que definen un comportamiento específico. Las componentes se asignan a los elementos de la escena llamados «GameObjects» y dotan a ese objeto de un comportamiento particular. En este artículo voy a compartir todo lo que sé acerca de las componentes en Unity que considero importante para poder mejorar tu manejo del motor Unity. Empecemos con una de las cosas más importantes:

En general, cualquier cosa que queramos hacer en Unity lo vamos a lograr a través de componentes asignadas a GameObjects.

Por ejemplo si queremos hacer que un objeto sea afectado por la gravedad, si queremos poder controlar el objeto con un mando o los inputs del teclado, si queremos reproducir sonidos, si queremos mostrar imágenes en pantalla. Todo esto y más se puede lograr utilizando distintas componentes asignadas a GameObjects de la escena en Unity.



Las componentes predefinidas en Unity

Fig. 1: Algunas componentes predefinidas del motor Unity.

El motor Unity trae definidas por defecto una amplia variedad de componentes que logran distintos comportamientos, podemos verlas al seleccionar un GameObject de la jerarquía y en el inspector hacer clic en el botón «Add Component«, que se observa en la figura 1, allí tendremos todas las componentes disponibles ordenadas en distintas secciones dependiendo de la tarea que hagan.

Algunos ejemplos de estas componentes predefinidas son las componentes de tipo AudioSource que se encargan de reproducir sonidos, las componentes SpriteRenderer permiten mostrar sprites (imágenes) en pantalla, una componente MeshRenderer puede mostrar un modelo 3D en pantalla y una componente AnimatorController puede controlar un conjunto de animaciones y las distintas transiciones entre ellas.

Cómo crear nuevas componentes en Unity

Las componentes en Unity no son otra cosa que scripts de programación, en el caso de las componentes que vienen definidas por defecto en Unity se trata de scripts que no se pueden modificar, pero la clave en todo esto es que NOSOTROS PODEMOS CREAR NUEVOS SCRIPTS y al hacerlo ESTAMOS CREANDO NUEVAS COMPONENTES EN UNITY, estos scripts se pueden asignar a los GameObjects, exactamente igual que las componentes que vienen por definidas por defecto.

Al asignar un Script a un GameObject, un Script que no es otra cosa que una componente personalizada por nosotros, Unity ejecutará este Script, ejecutará sus instrucciones, lo que nos permitirá lograr cualquier cosa que queramos.

Para que Unity evalúe un script o una componente deben darse algunas condiciones que veremos a continuación.



Cómo hacer que una componente funcione en Unity

Para que una componente cualquiera haga su trabajo en Unity es necesario que se cumplan cuatro condiciones, a continuación vamos a listarlas y luego expandiremos la información sobre cada condición.

  1. La escena que está cargada durante la ejecución es la que contiene la componente.
  2. La componente debe existir en la escena.
  3. La componente debe encontrarse ACTIVA.
  4. La componente debe estar asignada a un GameObject activo de la escena

Si se cumplen estas cuatro condiciones la componente hará la tarea que tiene programada.

Hay que tener en cuenta que en algunos casos la componente podría parecer que no está haciendo su trabajo, tomemos el caso de un AudioSource que reproduce un sonido, podría haber momentos en los que el sonido no se reproduce, pero esto no significa que la componente no esté funcionando, si se cumplen las cuatro condiciones mencionadas anteriormente Unity está evaluando su comportamiento, solo que su comportamiento en ese momento quizás es no reproducir el sonido hasta que se dé la orden.



Condición 1: La escena donde se encuentra la componente debe estar cargada

Una aplicación hecha en Unity puede estar dividida en distintas escenas y cada escena tiene definido sus propios elementos. Al iniciar una aplicación en Unity se cargará automáticamente la escena que se haya definido con el índice 0 en la configuración de Build Settings de Unity y además en todo momento podemos pasar de una escena a la otra, por ejemplo al darle a un botón «Jugar» del menú principal podemos cargar otra escena donde está montado el juego.

Fig. 2: Escenas que se agregaron a la compilación del juego o aplicación. Al iniciar se cargará automáticamente la escena 0.

Las componentes en Unity se asignan a los GameObjects y los GameObjects pertenecen a una escena en particular, por lo tanto si la componente que nos interesa se encuentra en una escena que no está cargada en un determinado momento, entonces su comportamiento no se estará ejecutando, simplemente porque esa componente no existe en ese preciso momento.

Condición 2: La componente debe existir en la escena

Fig. 3: Al asignar un Script a un GameObject se crea una instancia de la componente que define el Script.

Para que una componente ejecute su comportamiento debe existir en la escena, esto significa que tenemos que «instanciarla», crear una instancia de la componente que queremos utilizar. La forma más simple de hacer esto es elegir un GameObject apropiado (o crear uno) y luego en el inspector, con el botón «Add Component», agregar la componente que queremos usar.

Este procedimiento para agregar una componente también puede hacerse a través de código, es decir desde un script podemos crear una instancia de una componente y asignarla a cualquier GameObject que queramos, para esto último necesitamos contar con la referencia del GameObject al que queremos asignarle la componente.

Si la componente que nos interesa no está instanciada, Unity no va a evaluar su comportamiento.



Condición 3: La componente debe encontrarse activa en la escena

Fig. 4: Casilla de activación de un componente en Unity.

En general las componentes en Unity, salvo algunas excepciones, tienen una casilla de habilitación que nos permite determinar si la componente se encuentra activa o inactiva, esto lo podemos ver en el inspector al seleccionar un GameObject, en la esquina superior izquierda de cada componente se encuentra esa casilla de habilitación, si está marcada la componente se encuentra activa, si está desmarcada la componente se encuentra inactiva.

Hay que tener en cuenta que el estado de activación puede modificarse a través de código, es decir dentro de un Script, si tenemos la referencia de ese componente, podemos activarlo o desactivarlo cuando lo necesitemos. Aquí tengo un vídeo en el que muestro cómo hacerlo.

Nota: La casilla de activación de un Script que hayamos creado no estará presente en el inspector si en el script no tenemos definida ninguna de las funciones internas de Unity (Awake, Start, Update, …). Tener en cuenta que estoy en la versión de Unity 2021.3.18f1, no estoy seguro si esto se cumple para versiones anteriores y no estoy seguro, aunque es probable, que si se cumpla para versiones posteriores.

Lee esto si tienes conocimientos de programación orientada a objetos

Las componentes en Unity pertenecen a la clase Component, en la jerarquía de clases existen clases como Behaviour o Renderer que heredan directamente de la clase Component, en este tipo de componentes la casilla de habilitación que vemos en el inspector muestra el estado de una variable interna llamada «enabled», una variable que se encuentra definida en los Scripts que heredan su comportamiento de clases como Behaviour o Renderer.
Tomemos el caso de los objetos Behaviour, estos objetos son componentes pero no todos los componentes son Behaviours, por ejemplo un componente AudioSource es un Behaviour y por lo tanto tiene su casilla de habilitación. Pero hay otros componentes como Transform o Rigidbody que heredan directamente de Component y por esa razón no vemos la casilla de habilitación en el inspector.



Condición 4: La componente debe estar asignada a un GameObject activo en la escena

Los GameObjects de la jerarquía pueden encontrarse activos o inactivos en la escena. Podemos cambiar el estado de un GameObject seleccionándolo y en el inspector, utilizar la casilla que se encuentra arriba del todo a la izquierda, si esa casilla está marcada el GameObject está activo en la escena mientras que si está desmarcada el GameObject está inactivo en la escena. También es posible activar y desactivar un GameObject a través de código.

Fig. 5: Casilla de activación de un GameObject en Unity.

Si un GameObject se encuentra activo en la escena, Unity ejecutará automáticamente algunas funciones pertenecientes a las componentes activas que estén asignadas a ese GameObject, las funciones más conocidas pueden ser la función Awake, Start, Update y FixedUpdate, pero hay muchas otras funciones que forman parte del ciclo de inicialización y actualización de Unity.

Si el GameObject no se encuentra activo no se ejecutarán automáticamente estas funciones sobre las componentes que tenga asignadas el GameObject, sin embargo esto no quiere decir que no podamos usar esas componentes, por más que una componente esté inactiva, podríamos accederla y leer algun parámetro que nos interese.

Fig. 6: Estructura jerárquica de GameObject dentro de una escena en Unity.

En Unity se puede establecer una jerarquía entre distintos GameObjects, es decir un GameObject puede actuar como padre de otros GameObject, es decir puede ser un parent, mientras que los GameObjects hijo serían sus childs. Los childs de un GameObject se verán afectados por algunas cosas que le ocurran a sus parents, por ejemplo si trasladamos el objeto parent, todos sus childs se moverán en conjunto. Este comportamiento también ocurre con el estado de activación del GameObject, si el parent se desactiva, todos sus childs (y los childs de sus childs) se desactivarán también. Por esta razón, para que una componente funcione en Unity, no solo el GameObject al que está asignada tiene que estar activo, sino todos los GameObjects que se encuentren jerarquía arriba.



Introducción

Antes de comenzar con los trucos y consejos útiles para el manejo de Blender les comparto brevemente mi historia con Blender.

Hace unos años tenía una seria adicción con el programa Blender que se utiliza para crear modelos 3D y animaciones, lo usaba un mínimo de 4 horas diarias intentando recrear todo tipo de cosas que pasaban por mi cabeza, particularmente nada artístico pero si era capaz de crear estructuras, muebles y otro tipo de objetos basandome en imágenes de referencia.
Modelar en 3D era algo que hacía que me sorprenda a mi mismo de mis propias capacidades, cada vez que se completaba un renderizado me sentía muy orgulloso de mi creación. En retrospectiva no hacía grandes cosas pero eran cosas que yo mismo había hecho a partir de la nada y eso era increíble.
Tanto tiempo y esfuerzo dedicado a modelar en 3D con Blender y texturizar con Substance Painter dieron sus frutos y hoy puedo incluir esas capacidades como parte de mi trabajo como desarrollador freelance.

A continuación vamos a repasar 10 trucos y consejos útiles a la hora de utilizar Blender que me han ayudado a agilizar y mejorar el proceso de modelado, permitiéndome lograr tareas más rápido o conseguir mejores resultados.

#1 – Enfocar la cámara en el objeto seleccionado en Blender

Comenzamos con un atajo para centrar la vista o también la cámara de renderizado sobre el objeto que tenemos seleccionado. Un truco sumamente importante ya que es algo que mejora muchísimo la agilidad en el manejo de Blender. Con este atajo puedes decirle adiós a todo ese tiempo en el que intentas ubicar correctamente la cámara sobre un modelo 3D o incluso sobre un vértice, el centro de una arista o de una cara.

Para utilizarlo simplemente selecciona un objeto o un elemento del mesh y pulsa el punto del teclado numérico, verás como la cámara se centra en el elemento seleccionado y además, al rotar la cámara, el elemento seleccionado es el centro de rotación.

#2 – Ocultar todos los objetos o la geometría excepto lo que seleccionamos en Blender

Si estamos trabajando en un proyecto en Blender que tiene muchos objetos o un objeto en particular que tiene un mesh particularmente complejo, puede resultar muy útil ocultar temporalmente ciertos objetos y dejar visible solo lo que se necesita para trabajar. Con este simple atajo podrás ocultar fácilmente los objetos seleccionados en Blender y cuando lo necesites volver a revelar todos los objetos ocultos.

Para aislar elementos en Blender simplemente selecciona el objeto o elemento del mesh que quieres aislar y presiona SHIFT+H, esto ocultará todos los demás elementos que no se hayan seleccionado. Para volver a hacer visibles todos los elementos ocultos presiona ALT+H.

#3 – Tip para emparentar y desemparentar objetos rápidamente en Blender

Cuando emparentar objetos uno de ellos pasa a ser el objeto padre y el o los otros objetos que elijamos pasan a ser los hijos, esto hace que los objetos hijos se vean afectados automáticamente por las transformaciones que reciba el objeto padre, por ejemplo un movimiento aplicado al padre hará que todos los hijos se muevan en conjunto, lo mismo ocurre para las rotaciones y cambios de escala.

Para emparentar un objeto o un conjunto de objetos a otro de manera rápida en Blender tienes que ir a la ventana Outliner donde se encuentran todos los objetos, seleccionar los que quieres emparentar y luego arrastrarlos al objeto al que quieres emparentarlos mientras mantienes pulsada la tecla SHIFT, opcionalmente puedes pulsar ALT para mantener la transformación de los objetos emparentados.

#4 – Renderizar imagen con transparencia en Blender (aplica a Cycles y Eevee)

En muchas ocasiones resulta muy útil renderizar solo las partes visibles de un modelo 3D y que el resto del renderizado sea transparente, como por ejemplo cuando queremos crear un GIF de uno mismo bailando y colocarlo en un artículo sobre consejos para Blender.

En la ventana de propiedades ve a la pestaña de propiedades del renderizado y ahí dirígete a la sección «Film», encontrarás una casilla llamada «Transparent» que al marcarla hará que las partes del render en las que no haya modelo 3D queden transparentes. Asegúrate de usar un formato de imagen apropiado que admita transparencia, como por ejemplo PNG.

#5 – Ver las normales de un modelo 3D en Blender

Las normales de un modelo 3D son un elemento matemático que permite saber en qué dirección está apuntando una determinada cara de un modelo 3D, a veces en el proceso de modelado ciertas normales pueden quedar invertidas, es decir mirando hacia el interior del modelo 3D y esto puede traer problemas con el shading, es decir problemas en la visualización de un material aplicado al modelo 3D, también comportamientos erráticos con las fuentes de luz y también surge otro problema importante si estamos creando estos modelos 3D para utilizar en motor gráficos como Unity, en este motor los modelos 3D se renderizan con «backface culling», esto quiere decir que si tenemos una cara invertida en el motor gráfico será invisible y podremos ver a través de ella, para solucionar esto basta con corregir las normales del modelo 3D, pero primero necesitamos verlas.

Para activar las normales de un modelo 3D es necesario estar en el MODO EDICIÓN. En la esquina superior derecha de la ventana Viewport pulsamos la flecha que despliega la ventana «Viewport Overlays», casi al final de la misma encontraremos la sección de «Normales» en la que tenemos 3 íconos para visualizar las normales, por lo general elijo mostrarlas en el centro de las caras. Además podemos ajustar la longitud de las normales.

#6 – Conocer la cantidad de vértices, aristas y caras totales y de un objeto particular en Blender

Cuando estamos creando modelos 3D nos puede interesar conocer información acerca de la geometría de los objetos que estamos creando, por ejemplo cuantos vértices, aristas, triángulos o caras tiene nuestro modelo, esto nos puede ayudar a determinar si hay algún problema con vértices duplicados y también llevar un control de cuantos polígonos tiene nuestro modelo 3D, si estamos creando modelos 3D para usar en un motor gráfico como Unity puede interesarnos mantener la cantidad de polígonos dentro de un número razonable acorde al modelo que estamos creando, especialmente si el objetivo es para una aplicación móvil o de realidad virtual, en las que hay ciertas limitaciones con el hardware.

Para mostrar la información de cantidad de vértices, aristas, caras y objetos en Blender nos dirigimos a la esquina superior derecha de la ventana Viewport, pulsamos la flecha que despliega la ventana «Viewport Overlays» y marcamos la casilla «Statistics» que se encuentra en la parte superior de dicha ventana.

#7 – Aplicar un mismo material a otros objetos en Blender

Cuando seleccionamos un objeto y queremos aplicarles un color o darle una apariencia metálica por ejemplo, lo que hacemos es crear un nuevo material, que por defecto se inicia con el Shader «Principled BSDF» y tenemos distintos valores para poder configurar el material a nuestro antojo. ¿Pero qué pasa si tenemos un segundo objeto y queremos que tenga el mismo material? Podríamos estar tentados a crear un nuevo material y configurarlo con los mismos parámetros, incluso es posible copiar los parámetros de un material y aplicárselos a otro.

Pero hay una alternativa mejor, en Blender podemos hacer que dos objetos tengan aplicados exactamente el mismo material, es decir que uno o varios slots de materiales estén apuntando a la referencia de un mismo material, de esta forma podemos crear una instancia particular de un material que podríamos llamar «Madera de Pino» por ejemplo y reutilizar ese mismo material en todos los objetos que necesiten la textura de madera de pino, esto no solo evita que tengamos muchas copias innecesarias de un material sino que además nos permite modificar el material y que los cambios se apliquen automáticamente en todos los objetos donde se utilice ese material.

En este caso el vídeo es más ilustrativo pero vamos a intentar resumir el procedimiento. Con un objeto seleccionado vamos a la pestaña Materiales (esfera con textura a cuadros), luego si pulsamos el signo + lo que vamos a hacer es crear un nuevo «Slot» para un material dentro de nuestro objeto, aquí hay dos opciones, una hacer click en «New» lo que crea una nueva instancia de un material, totalmente independiente de las demás, o sino podemos seleccionar un material ya existente (lo que nos interesa en este caso), para esto pulsamos el ícono a la izquierda del botón «New» y seleccionamos de la lista el material que queremos asignar al slot.

#8 – Mostrar los huesos de animación siempre al frente de los demás objetos en Blender

A la hora de crear animaciones con Blender usando huesos de animación resulta muy útil ser capaz de ver estos huesos en todo momento por más que se encuentren ocultos dentro de otro objeto o estén obstruídos por un objeto.

Con el objeto «Armature» seleccionado vamos a la pestaña «Object Data properties» (la cual tiene ícono de humanoide y se encuentra por encima de la pestaña con el ícono de hueso), luego nos dirigimos a la sección «Viewport Display» y marcamos la casilla «In Front».

#9 – Crear Edge Loops rápidamente en Blender

Cuando ganamos algo de experiencia con Blender nos encontramos con el concepto de «Edge Loop», básicamente se trata de un conjunto de vértices sobre una superficie que están conectados entre sí y el último vértice del conjunto se vuelve a conectar con el primero, la clave es que de todas las posibles conexiones que se pueden trazar y que cumplan estas condiciones, el Edge Loop es como el bucle que está conectado de la forma más coherente en relación a los demás conjuntos de vértices de los alrededores, es un concepto algo difícil de explicar pero es fácil de entender una vez que empezamos a trabajar con ellos. Un ejemplo de edge loop puede ser uno de los anillos que forma una esfera o un donut en Blender (el nombre correcto es torus pero parece un donut), cada anillo es un conjunto de vértices conectados formando un bucle.

Para crear rápidamente un Edge Loop en Blender, seleccionamos un objeto, entramos en el MODO EDICIÓN y pulsamos CTRL+R, luego acercamos el cursor hacia la parte de la geometría en donde nos interesa agregar el bucle de vértices, en este punto podemos girar la rueda del mouse para incrementar la cantidad de bucles a agregar o ingresar manualmente una cantidad por teclado.

#10 – Seleccionar Edge Loops fácilmente y removerlos en Blender

Existe una forma rápida de seleccionar Edge Loops lo que nos permite aplicar transformaciones sobre el modelo, por ejemplo aumentar el tamaño de un Edge Loop en particular o desplazar dicho Edge Loop en una dirección y también podemos deshacernos de ese Edge Loop de una forma en la que conservemos el resto del modelo intacto, esto último es especialmente útil cuando queremos disminuir drásticamente la cantidad de polígonos de un modelo 3D para poder utilizarlo en un motor gráfico como Unity por ejemplo.

Para seleccionar rápidamente un Edge Loop en Blender tenemos que estar en el modo edición de un objeto, luego mantenemos presionado ALT Izquierda y hacemos clic izquierdo sobre una de las aristas que pertenezca al Edge Loop que nos interesa seleccionar, si pulsas un vértice del Edge Loop puede que selecciones otro Edge Loop que atraviesa el mismo vértice, por lo que para asegurarse de seleccionar el correcto es mejor pulsar sobre las aristas.

Anne y los mapaches

Sinópsis

Anne lleva una vida pacífica en su granja, atendiendo sus cultivos y cuidando su jardín. De pronto la paz se acaba ya que una gran cantidad de mapaches aparecen en su granja y desordenan todo cuanto encuentran. Anne ha trabajado muy duro en su granja y no piensa quedarse de brazos cruzados.

Cómo jugar

Controla a Anne pulsando las teclas WASD o las flechas de movimiento. Recoge los objetos con la tecla E. Pon la granja en funcionamiento y si tienes tiempo encuentra los vegetales que desordenaron los mapaches. Cada puesto se normaliza con un objeto en particular de modo que tendrás que tener este objeto al momento de ahuyentar a los mapaches y normalizar el puesto.

Desarrollado para la Game Jam Ludum Dare 51 con el tema "Cada 10 segundos" por:

MARTÍN HEINTZ

MUSIC COMPOSER

GAMEDEVTRAUM

UNITY DEVELOPER

STEFF

UI/UX DESIGN

ÁLVARO

GAME DESIGN

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