En este artículo vemos un prototipo de inteligencia artificial para enemigos en Unity.
Dichos enemigos estarán en distintos estados de acuerdo a lo que ocurra durante la partida. Los estados son «Guardia», «Buscando», «Atacando», «Cubriéndose».
La idea de este prototipo surgió porque una persona me escribió en los comentarios del canal preguntando cómo se podría hacer que los enemigos te detecten, se pongan en alerta, avisen a los demás y ataquen.
Para responder a su pregunta cree un proyecto en Unity proponiendo una aproximación al problema e hice un video explicando cómo funcionaba este prototipo de inteligencia artificial para enemigos. Lo puedes ver a continuación.
Puedes descargar los archivos del proyecto para importar y ver en detalle los Scripts.
En la descarga tenemos un paquete de Unity que podemos importar haciendo clic derecho en la carpeta Assets y con la opción Import Package > Custom Package.
Al importarlo tendremos todos los archivos del prototipo, en la carpeta Assets tenemos los Scripts, prefabricados y escenas que componen la solución. El enemigo inteligente es el Third Person Controller versión inteligencia artificial de Standard Assets al cual se le aplica el Script enemigo que modela su comportamiento, esto hace que sea capaz de detectar al jugador, perseguirlo, dispararle y buscarlo en caso de que lo haya perdido de vista.
¿Qué vamos a hacer con esto?
Por el momento se trata simplemente de un prototipo que hice en un fin de semana y que cumple con el problema que me plantearon. Sin embargo todo puede mejorarse, en base a este prototipo podemos crear un sistema más complejo, mejorar el código, modularizar el código de manera mas coherente, crear un mejor modelo para el arma, separar el rango de visión del Script Enemigo, mejorar en general los estados individuales.
¿Qué es la Inteligencia Artificial?
Antes de empezar a explicar cómo funciona el prototipo me gustaría hablar un poco sobre el concepto de inteligencia artificial, vamos a acotar el alcance ya que hay varias definiciones.
En informática la inteligencia artificial hace referencia a un autómata (o máquina) que es capaz de percibir su entorno y tomar decisiones apropiadas de acuerdo al comportamiento que se le haya programado.
En nuestro caso queremos crear enemigos inteligentes en Unity, así que para hacerlo debemos pensar cuáles son las características fundamentales de un enemigo, su comportamiento y su manera de percibir el entorno.
Por supuesto debemos establecer límites para el alcance de la inteligencia artificial para el enemigo, por ejemplo nuestro enemigo no será capaz de aprender de su entorno.
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 programar el funcionamiento de la interfaz de usuario creada en el video 5 de la serie del laberinto. Si no leiste el artículo anterior, en resumen creamos una interfaz de usuario en Unity, que contará con la funcionalidad mínima para que el usuario pueda interactuar con el juego.
En el artículo anterior diseñamos una interfaz de usuario simple para que el usuario puede controlar los aspectos fundamentales de nuestro juego, como pueden ser iniciar la partida, saber cuánto tiempo le queda, etc.
De momento vamos a utilizar el Script GameControl para manejar la interfaz de usuario. En capítulos siguientes delegaremos esta responsabilidad a un nuevo Script.
Para resumir los elementos de la interfaz de usuario tenemos dos pantallas, una para el menú principal la cual contiene sólo un botón que sirve para iniciar el juego. La segunda pantalla es para la partida y tiene un elemento texto que mostrará la cantidad de tiempo que queda para terminar la partida.
Resolución
En el Script GameControl definimos tres GameObjects serializados, uno para la cámara que está activa en el menú principal, otro para la interfaz del menú y otro para la interfaz de la partida.
Los tres GameObjects definidos están resaltados en la figura 1.
Fig. 1: Se definen tres GameObjects serializados para la cámara y las pantallas de la interfaz.
En la figura 2 podemos observar que estos nuevos campos aparecen en el inspector.
Fig. 2: Los tres GameObjects aparecen en el inspector.
Tomamos los objetos creados en el video anterior y los arrastramos a los campos en el inspector. Estos objetos son la cámara, el GameObject MainMenu y el GameObject Game.
Fig. 3: Tomamos los GameObjects de la jerarquía y los llevamos al inspector.
Fig. 4: En el inspector colocamos cada GameObject en su espacio correspondiente.
Métodos startGame y endGame
Vamos a definir dos métodos, uno se va a llamar «startGame» y el otro «endGame». El método startGame tendrá la función de hacer todo lo necesario para que la partida empiece, es decir poner el timer en cero, colocar el prefab del personaje, colocar el pedestal en una posición aleatoria del escenario y demás. Tengamos en cuenta que estas acciones probablemente estarán definidas en otros métodos.
En el método endGame eliminamos todos los objetos del escenario, vamos al menú principal y demás.
Fig. 5: Se definen los métodos startGame y endGame.
En los métodos vamos a activar y desactivar los elementos correspondientes, como se observa en la figura 6. Esto tiene que ver con la interfaz de usuario.
Fig. 6: Dentro de cada método colocamos las instrucciones apropiadas.
Vamos a la jerarquía y seleccionamos el botón «Start», para observar sus propiedades en el inspector.
Fig. 7: Se selecciona el botón de start en la jerarquía.
Fig. 8: En el inspector hacemos clic en el signo más para agregar una función a OnClick().
En la componente Button hay un campo llamado OnClick(), vamos a hacer clic en el signo más de la esquina inferior derecha, en la figura 8 el cursor está sobre este botón.
Esto nos permitirá ejecutar métodos públicos dentro de las componentes del GameObject que asignemos.
En el campo que aparece vamos a colocar el GameObject Control.
Fig. 9: Aparece un espacio para colocar un GameObject.
Fig. 10: Colocamos el objeto Control que tiene asignado el Script GameController.
Ahora utilizando el menú desplegable, podemos acceder a las componentes y sus métodos.
Fig. 11: Con el menú desplegable se puede seleccionar un método de alguna de las componentes del objeto Control.
Como se trata del botón Start, estamos buscando el método StartGame que hemos definido en el Script GameControl, en la figura 12 podemos observar los atributos y métodos públicos del Script GameControl y vemos que StartGame no se encuentra entre ellos.
Fig. 12: El método startGame no aparece entre las funciones.
Esto se debe a que utilizamos visibilidad privada en la definición.
Para solucionar el problema, cambiamos el método a visibilidad pública, como se ve en la figura 14.
Fig. 13: Habíamos definido el método startGame como privado.
Fig. 14: Cambiamos la visibilidad a público.
Ahora podemos elegir que se ejecute este método cuando se haga clic en el botón Start.
Fig. 15: Ahora el método startGame aparece en las funciones.
Acomodo un poco la cámara para que se vea la pequeña escena de las puertas y entro en el modo juego para probar el funcionamiento del botón.
Fig. 16: Acomodamos la cámara del menú principal.
Fig. 17: Entramos en el modo juego para probar el botón.
Corrección de bug
Al entrar en modo juego, el personaje aparece en una de las puertas de manera aleatoria, pero por alguna razón los controles no funcionan correctamente, el personaje se arrastra por el suelo y la cámara gira en una dirección extraña.
Fig. 18: El juego inicia pero parece haber un problema con el personaje.
Fig. 19: El personaje aparece de costado y no puede moverse.
El proceso de Debugging puede llegar a ser algo tortuoso, por suerte tenemos herramientas para analizar el flujo del programa, descubrir las partes del código que funcionan correctamente, ver los valores de las variables y así hasta detectar el problema y poder corregirlo.
Sin entrar en detalles, porque sinceramente no recuerdo cómo descubrí el bug, el problema tenía que ver con la estructura de parentezco entre los GameObjects y la transformación herededa del GameObject padre.
Para corregir el problema en la instrucción para instanciar al personaje, dentro del método «placePlayerRandomly», cambio la rotación de local a global, como se observa en las figuras 20 y 21.
Fig. 20: El método placePlayerRandomly es el que se encarga de colocar al personaje en el escenario.
Fig. 21: En la instrucción instantiate cambiamos localRotation por rotation.
Con esto se corrige el problema y ya podemos utilizar el botón start para comenzar el juego.
Fig. 22: Ahora el personaje aparece correctamente en el escenario.
Algunos detalles antes de terminar
Cuando se entra en el modo juego, el prefab del controlador en primera persona captura el cursor, lo coloca en el centro de la pantalla y lo oculta.
Esto es un problema porque al regresar al menú principal cuando se termina la partida no podemos hacer clic en los botones. Así que la solución es desbloquear el cursor y hacerlo visible en el momento en que termina la partida, es decir en el método endGame().
Para descubrir cómo hacer esto vamos a analizar el Script MouseLook que utiliza el controlador en primera persona. En la figura 23 podemos ver este Script junto con su ruta para llegar a él.
Fig. 23: Abrimos el Script MouseLook dentro de FirstPersonCharacter de Standard Assets.
Error Fatal
Al intentar abrir MouseLook el editor falla y debe cerrarse perdiéndose todos los cambios no guardados! Es importante guardar en todo momento, el acceso CTRL+S es muy útil.
Fig. 24: Al abrir el script se produce un error y se pierden todos los cambios no guardados.
En el segundo intento fuimos capaces de acceder el Script. Estamos buscando una instrucción relacionada al Cursor.
Vemos que en las líneas 93 y 94 de la figura 26 hay un par de instrucciones que parecen ser las indicadas así que las copiamos.
Fig. 25: Revisamos el script MouseLook en busca de las instrucciones que desbloquean el mouse.
Fig. 26: Las dos instrucciones seleccionadas desbloquean el cursor y lo hacen visible.
Luego vamos al Script GameControl y pegamos estas instrucciones al final del método endGame que se ejecutará cuando el tiempo se agote.
Fig. 27: Pegamos las instrucciones al final del método endGame.
Finalizar la partida
También vamos a darle al usuario la posibilidad de terminar la partida cuando lo desee utilizando la tecla Escape.
Esto lo hacemos con el método GetKeyDown de la clase Input.
Fig. 28: Ejecutamos el método endGame al presionar la tecla escape.
Conclusión
En el artículo analizamos cómo programar la interacción entre los elementos de la interfaz de usuario y el Script GameControl.
En el caso del botón ya está programada la funcionalidad para que ejecute métodos de los Scripts, pero debemos asegurarnos de que estos métodos tengan visibilidad pública.
Introducción
El sistema de coordenadas cartesianas recibe su nombre en honor al filósofo y matemático René Descartes, considerado el creador de la geometría analítica. Este sistema nos permite representar puntos tanto en el plano como en el espacio.
¿Qué es un plano cartesiano?
El plano cartesiano está definido por dos rectas perpendiculares entre sí, una horizontal y otra vertical, estas rectas se conocen como ejes cartesianos. Utilizando estos ejes podemos identificar cualquier punto del plano con un único par ordenado de números.
El eje horizontal se lo conoce como eje de ABSCISAS y normalmente es conocido como eje x (aunque podría usarse cualquier letra). El eje vertical es conocido como eje de ORDENADAS y normalmente es el eje y.
El punto donde se cortan los ejes de abscisas y ordenadas se conoce como ORIGEN de coordenadas o simplemente origen y es el punto (0,0) en el plano y (0,0,0) en el espacio.
En la siguiente figura se muestra un plano cartesiano con el punto (2,1) graficado.
Fig. 1: Plano cartesiano xy con el punto (2,1) representado.
Cuadrantes del plano cartesiano
Los cuadrantes son regiones características del plano definidas por los cambios de signos en los ejes coordenados. Son cuatro cuadrantes, el primero es la región superior derecha y se enumeran en sentido antihorario.
Fig. 2: Identificación de los cuadrantes del plano cartesiano.
Como se observa en la figura 2, los cuadrantes están definidos en las siguientes regiones.
Primer cuadrante: { x > 0 , y > 0 }
Segundo cuadrante: { x < 0 , y > 0 }
Tercer cuadrante: { x < 0 , y < 0 }
Cuarto cuadrante: { x > 0 , y < 0 }
Representar funciones en el plano cartesiano
El plano cartesiano además nos sirve para representar funciones, es decir reglas que asignan cada punto del eje x a un punto del eje y. En las figuras 3 a 6 se muestran algunos ejemplos de gráficos de funciones en el plano cartesiano.
Fig. 3: Gráfico de una función lineal.Fig. 5: Gráfico de una función cúbica.
Fig. 4: Gráfico de una función cuadrática o parábola.Fig. 6: Gráfico de una función seno de x.
Sistema cartesiano en Unity
Las coordenadas cartesianas se utilizan en varios lugares en Unity, por ejemplo para definir la posición de un GameObject en el plano o el espacio. Pero para el propósito quise hacer un graficador de funciones simple utilizando esferas que se mueven en el eje x y su posición en el eje y se calcula utilizando la expresión de la función.
Los botones nos permiten cambiar la expresión de la función
Fig. 7: Las esferas se mueven de acuerdo a una función lineal.Fig. 9: Las esferas se mueven de acuerdo a una función cuadrática.
Fig. 8: Las esferas se mueven de acuerdo a una función senoidal.Fig. 10: Las esferas se mueven de acuerdo a una función senoidal en un tramo y lineal en otro.
Conclusión
Las coordenadas rectangulares se utilizan con frecuencia en Unity.
Comprender cómo se utiliza el plano cartesiano para representar puntos y funciones en el plano, junto con conocimientos de geometría analíticas nos puede ayudar a crear nueva e interesantes soluciones.
ACTUALIZACIÓN IMPORTANTE
Este artículo pertenece a una serie de vídeos antiguos, actualmente he creado una nueva solución que permite cargar cualquier escena pulsando un botón y solo se necesita escribir el nombre de la escena que se quiere cargar, además puedes reutilizar esta solución simplemente arrastrando el script a otro botón y cambiando el nombre de la escena que quieres cargar, permitiendo por ejemplo crear un menú de selección de escenas.
Dejo la nueva información a continuación y más adelante encontrarás el artículo antiguo.
Descarga la solución en mi página de ITCH.IO
Se trata de un paquete de Unity que puedes descargar e importar y su funcionamiento se describe en el vídeo aquí debajo, te permitirá fácilmente cargar escenas en Unity al pulsar botones.
El paquete es gratis pero tienes la opción de indicar un precio para colaborar con esta página.
En el siguiente vídeo muestro cómo cargar una escena por nombre al pulsar un botón
El paquete que te descargas más arriba funciona de forma muy similar a como se muestra en este vídeo.
Este es el último ejercicio de la Serie Fundamental de Unity, en este artículo vamos a ver cómo cambiar de escena en Unity en tiempo de ejecución.
Entender cómo cambiar de escena es importante porque nos permite separar el contenido de nuestro juego en partes, por ejemplo el menú principal por un lado y el juego en sí por otro. Otro ejemplo podría ser un pueblo con distintos edificios en los que se puede entrar, el pueblo puede estar construido en una escena y luego utilizar múltiples escenas para cada edificio, o utilizar una escena para todos los edificios y elegir el apropiado en el momento de cargar la escena.
Cada escena en Unity contiene su propia jerarquía de GameObjects, dependiendo de nuestras necesidades podremos utilizarlas de distintas formas.
Algunos ejemplos simples de utilización de escenas son usar una escena para el menú y otra para el juego, cada escena es un nivel de nuestro juego, hacer todo en una única escena, etc.
En nuestros proyectos probablemente debamos hacer cambios de escena, es por eso que considero forma parte de una de las cosas fundamentales de Unity.
Estación LoadScene del GameDevLab
Para aprender a cambiar una escena en Unity vamos a utilizar la estación LoadScene del GameDevLab, la cual consiste en un pozo extraño con aspecto de desintegrar la materia y enviarla a través del espacio-tiempo.
Fig. 1: Vista frontal de la estación LoadScene.
Fig. 2: Interior del pozo de la estación LoadScene.
Fig. 3: Interior del pozo de la estación LoadScene.
En el fondo del pozo hay una cápsula (figura 4) que tiene asignado un Collider en modo trigger y un Script que que se encargará de detectar el personaje y enviar un mensaje al Script que tenemos que completar.
Fig. 4: En el fondo hay una cápsula con Collider en modo trigger.
La estación completa está contenida en el GameObject «#10 LoadScene» de la jerarquía, este GameObject tiene asignado el Script «LoadScene» el cual es el Script que tenemos que completar en este ejercicio.
Fig. 5: Jerarquía del proyecto.
Fig. 6: Componentes de la estación LoadScene.
El Script para completar contiene un solo método público llamado «teleport», este método será ejecutado por el Script de la cápsula cuando se detecte al personaje en el fondo del pozo.
Lo que vamos a hacer es cambiar la escena dentro del método «teleport», para esto vamos a la carpeta escenas del pack de la serie fundamental y vemos que hay dos escenas (figura 8), una es el GameDevLab y la segunda escena es GameDevDimension, esta última es la que vamos a cargar utilizando su nombre.
Fig. 8: Escenas del proyecto, GameDevLab y GameDevDimension.
Resolución
Para empezar tenemos que importar el namespace UnityEngine.SceneManagement
Fig. 9: Librerías importadas en la parte superior del Script.
Luego para cambiar la escena en Unity ejecutamos un método estático de la clase «SceneManager», el método LoadScene, al cual le pasamos como parámetro un String con el nombre de la escena que queremos cargar.
Fig. 10: Método para cambiar de escena en Unity.
Con esa simple instrucción podemos hacer el cambio de escena, pero para que esto funcione todas las escenas tienen que estar añadidas a la compilación, esto lo hacemos en File > Build Settings, abriendo cada escena y haciendo clic en Add Open Scenes.
Fig. 11: Se agregan las escenas en Build Settings.
Cuando el personaje cae al pozo se carga una nueva escena en la cual la cámara avanza automáticamente y los controles están inhabilitados, cuando la cámara llega al final del túnel se vuelve a cargar automáticamente la escena del GameDevLab.
Fig. 12: Al entrar en el modo juego y caer en el pozo se carga la escena GameDevDimension.
Fig. 13: Vista del túnel de la escena GameDevDimension.
Fig. 14: Vista del túnel de la escena GameDevDimension.
Fig. 15: Vista del túnel de la escena GameDevDimension.
Conclusión
Para cambiar una escena en Unity necesitamos importar la librería SceneManagement de UnityEngine y utilizamos un método estático de la clase SceneManager.
Nos podemos referir a una escena por su nombre o por su código de identificación que aparece en Build Settings, en este caso utilizamos el nombre.
Cargar una nueva escena es una tarea relativamente simple, el problema es que la nueva escena está predefinida y comienza siempre desde cero.
Por ejemplo una escena puede ser una casa con diez monedas, el personaje entra, toma las monedas y sale de la casa, es decir vuelve a la escena anterior. Si no hacemos nada al respecto, al volver a entrar en la escena de la casa estarán nuevamente las diez monedas en el interior.
Para prevenir esto tenemos que almacenar información del estado de las variables en la escena y leer esta información al comienzo de la escena.
Introducción
Siempre me gustaron las historias de terror y hace tiempo que quiero empezar a hacer narraciones para el canal tanto propias como de clásicos de terror.
Decidí dar el paso y comenzar con el libro «La llamada de Cthulhu» de H. P. Lovecraft, la cual es una historia relativamente corta de poco más de una hora de lectura.
Capítulo 1
Capítulo 2
Capítulo 3
Sobre el autor: H. P. Lovecraft
Howard Phillips Lovecraft nace el 20 de agosto de 1890 en Providence, Rodhe Island.
Al pequeño y solitario Howard le gustaba visitar lugares extraños y apartados para poder dar rienda suelta a su imaginación. En esos sitios recreaba situaciones históricas o se ensimismaba en la observación de pequeños detalles que pasaban inadvertidos al resto de las personas, pero que a Lovecraft le fascinaban.
Quizás una de las razones por las que le gustaba tanto alejarse era por el control estricto que ejercía su madre sobre él, diciéndole que no debía jugar con niños de menor categoría o insistiendo en que era feo y que nunca llegaría a triunfar.
Se le considera un gran innovador del cuento de terror y uno de los autores más influyentes del siglo XX en el género de la literatura fantástica.
Introducción
Los conjuntos numéricos se utilizan para agrupar números que tienen características similares. Es uno de los conceptos básicos de la matemática por lo que es importante conocer cuáles son estos conjuntos y qué características tienen los números que pertenecen a cada conjunto. En este artículo encontrarás información sobre los distintos tipos de números y un vídeo de mi canal de YouTube que resume la información.
En este vídeo se muestra un resumen completo sobre conjuntos numéricos
¿Para qué se usan los conjuntos numéricos?
Los conjuntos numéricos se utilizan para separar los números en distintas clases que tienen propiedades similares.
Esto debemos tomarlo simplemente como una forma de organización, en la cual, dado un número específico, decimos que ese número pertenece a un determinado conjunto.
Conjuntos numéricos
Los conjuntos numéricos básicos son los siguientes:
Naturales – ℕ
Enteros – ℤ
Racionales – ℚ
Reales – ℝ
Complejos – ℂ
Cada conjunto más general va englobando al conjunto anterior es decir que por ejemplo todos los números naturales son enteros, pero no todos los números enteros son naturales.
Conjunto de Números Naturales (ℕ)
Este conjuntos está compuesto por los números {1,2,3,…} (los puntos suspensivos indican que la enumeración continúa indefinidamente), estos números son todos positivos y representan magnitudes enteras, es decir no tienen parte decimal.
Conjunto de Números Enteros (ℤ)
Si a los números naturales agregamos el número 0 y los números negativos sin parte decimal obtenemos el conjunto de los números enteros. {…,-3,-2,-1,0,1,2,3,…}.
Con los números negativos podemos representar operaciones de sustracción, magnitudes faltantes, valores que se encuentran por debajo del cero de referencia y demás.
Algunos ejemplos son flujos salientes de dinero, es decir dinero que pagamos y se resta de lo que tenemos; temperaturas bajo cero se expresan como valores negativos de grados centígrados.
Conjunto de Números Racionales (ℚ)
El conjunto de los números racionales surge de hacer divisiones de dos números enteros. Por ejemplo 1 dividido 2 es una operación que da lugar a un número que es más pequeño que 1 pero más grande que 0.
Estos números se utilizan para representar magnitudes no enteras, por ejemplo variables de naturaleza continua como velocidad, peso, corriente eléctrica; expresar cantidades fraccionables por ejemplo medio kilo de harina son 0.5 Kg de harina.
Conjunto de Números Reales (ℝ)
Si al conjunto de los números racionales le agregamos el conjunto de los números irracionales obtenemos el conjunto de los números reales.
Los números irracionales surgen de realizar ciertas operaciones y no es posible expresarlos como el cociente entre dos números enteros.
Un ejemplo de este tipo de números es el conocido número Pi que se compone de infinitas cifras decimales.
Conjunto de Números Complejos (ℂ)
Si al conjunto de los números reales agregamos los números imaginarios, obtenemos el conjunto de números complejos. Todo número que elijamos será en términos generales un número complejo.
El número imaginario i es el resultado de la raíz cuadrada de -1.
Los números complejos se componen de una parte real y una parte imaginaria y son muy útiles para el estudio de circuitos eléctricos que involucran elementos capacitivos o inducitvos. También se utilizan en cálculos de transformadas de Fourier.
Conjuntos Numéricos en Programación
Modelado de los elementos del juego
Para modelar el comportamiento de distintos elementos en nuestro juego probablemente necesitaremos usar distintos tipos de números.
Por ejemplo un personaje puede tener un nivel de vida representado por corazones. Al recibir daño pierde un corazón y al tomar una poción curativa recupera un corazón. En este caso el nivel de salud es una magnitud entera.
También podemos pensar por ejemplo que cada corazón se puede fraccionar en cuatro partes y cada golpe recibido nos quita un cuarto de corazón. Además podría ser que un enemigo fuerte nos quite tres cuartos de corazón con cada golpe. En este caso los corazones los estamos pensando como magnitudes racionales y vamos a necesitar números decimales para representarlas.
Todo dependerá de cómo queremos que sea nuestro modelo.
De los conjuntos numéricos a la programación
Para representar los distintos tipos de números que necesitamos para construir modelos, contamos con elementos llamados variables, que son espacios en la memoria donde se almacena información.
Los lenguajes de programación nos ofrece distintas variables para representar distintos tipos de datos.
Para representar magnitudes enteras tenemos por ejemplo el byte, el short, int y long.
Cada uno tiene su propio rango de representación, por ejemplo el intnos permite representar números enteros que están entre -2,147,483,648 y 2,147,483,647. El short en cambio nos permite representar números entre -32,768 y 32,767, el cual también es un rango bastante extenso para un gran número de aplicaciones.
En mi caso por una cuestión de simplicidad suelo utilizar variables tipo int para representar magnitudes enteras, aunque en muchos casos esta variable está sobredimensionada, por ejemplo si se utiliza la variable entera para representar la edad de una persona.
Vídeo sobre cómo generar número enteros de manera aleatoria en C#
Variables Reales
Para representar valores decimales en computación se utiliza el sistema de punto flotante.
Tenemos las variables tipo float de 32 bits, las variables tipo double de 64 bits y las variables tipo decimal de 128 bits. Cada una tiene un determinado rango de representación y precisión.
En computación no se pueden almacenar infinitos dígitos, de modo que los números irracionales se representarán como números racionales y surgirá un error de truncamiento.
Variables Complejas
Los números complejos se pueden representar con dos números reales, uno es la parte real y otro la parte imaginaria, esta última estará multiplicada por el número i. Como se expresa en la siguiente ecuación:
Z = X + i . Y
Siendo X e Y números reales y Z un número complejo.
Podemos representar los números complejos utilizando dos floats con nombres significativos para las partes real e imaginaria, podemos usar vectores de dos componentes, o podemos utilizar la clase Complex de C#.
Conclusión
Los conjuntos numéricos se utilizan para agrupar distintos tipos de números que tienen características similares.
Conocer estos conjuntos y sus propiedades nos ayudará en la construcción de modelos computacionales.
Para implementar los números en programación contamos con elementos llamados variables. Existen distintos tipos de variables para representar números enteros y racionales, cada tipo tiene su propio rango de representación.
Introducción
En este artículo vamos a ver una forma de rotar objetos en Unity de tal forma de tener el control de la dirección de rotación en el mundo. Vamos a utilizar el método Rotate de la clase Transform, esto modificará directamente los parámetros de rotación que se observan en la parte superior del inspector con el GameObject seleccionado.
En el siguiente vídeo vemos una forma de ROTAR OBJETOS en Unity alrededor de los ejes globales
Para poder describir matemáticamente tenemos que conocer directa o indirectamente los siguientes parámetros:
Eje de rotación
Cantidad de ángulos a girar
Sentido de giro
Imaginemos que el objeto que queremos rotar es una esfera. La tomamos en nuestras manos y clavamos una varilla haciendo que pase por el centro exacto de la esfera. Luego tomamos la varilla y la orientamos de norte a sur paralela al suelo. Finalmente hacemos que gire media vuelta en sentido anti horario.
¿Pudiste visualizarlo en tu mente?
Mi descripción de la rotación fue exacta aunque quizás no fue redactada de la mejor manera para que se entienda. Sin embargo vamos a asumir que todos pudimos imaginar exactamente lo mismo.
En primer lugar podemos decir que el eje de rotación es la varilla. Si nos ponemos estrictos el eje de rotación es una línea imaginaria que pasa por los dos extremos de la varilla.
La cantidad de ángulos a girar es media vuelta, esto matemáticamente son 180° o π radianes.
El sentido de giro es antihorario o en contra de las manecillas del reloj.
Estos tres parámetros se pueden describir completamente utilizando un vector, que es lo que vamos a hacer en este artículo.
Estación de trabajo
Vamos a usar la estación de rotación que consiste en tres engranajes que están orientados en el sentido de los tres ejes x,y,z del mundo.
Fig. 1: Vista frontal del mecanismo de engranajes a utilizar.
Los tres engranajes están colocados perpendicularmente entre sí.
Fig. 2: Vista lateral del mecanismo de engranajes a utilizar.
En la jerarquía los podemos encontrar dentro del GameObject «#9 RotateObjects». Tienen los nombres GearX, GearY y GearZ indicando el eje de rotación de cada engranaje.
Fig. 3: En la jerarquía tenemos un GameObject para cada engranaje.
El script para completar se llama «RotateObjects» y está asignado al GameObject con el mismo nombre en la jerarquía.
En la figura 4 vemos que los tres GameObjects de la jerarquía ya están asignados en los campos del inspector.
Fig. 4: Los engranajes están asignados en los campos del Script Rotate Objects.
Al abrir el Script nos encontramos con el método FixedUpdate definido y dentro unos comentarios que dicen que ahí se debe rotar los engranajes.
El método FixedUpdate como su nombre lo indica es una actualización del estado del objeto pero ejecutada cada un cierto tiempo fijo.
Esto quiere decir que por más que el frame rate del juego cambie, tendremos la misma cantidad de ejecuciones de este método por segundo.
Colocaremos en el método FixedUpdate una instrucción que hará que los engranajes giren una determinada cantidad de ángulos en algún sentido respecto de un eje. Esto significa que en cada llamada de FixedUpdate se aplicará la rotación, lo que generará un movimiento continuo de rotación.
Fig. 5: Campos definidos en el Script RotateObjects.
Fig. 6: Script al abrirlo por primera vez. El método FixedUpdate está definido.
Resolución del problema
El método Rotate de la clase Transform tiene distintas versiones, vamos a usar una en la que el primer parámetro es el vector que expresa completamente la rotación y en el segundo parámetro indicamos el tipo de coordenadas a utilizar, coordenadas globales que son únicas en todo el mundo del juego o coordenadas locales que son propias de cada objeto en particular, que serían como si cada objeto tuviese su propio norte.
Fig. 7: Instrucciones para rotar cada engranaje respecto de su propio eje.
Por propiedades matemáticas de los vectores reescribí el vector como el producto de su módulo o norma multiplicado por un vector unitario (norma 1) que describe la dirección.
Esto nos permite separar por un lado la dirección de la rotación y por otro lado la cantidad de ángulos a rotar.
Fig. 8: Modificando la variable en el inspector podemos cambiar la velocidad del conjunto de engranajes.
Otra ventaja de expresar el vector de esa forma es que podemos cambiar el sentido de giro simplemente utilizando valores negativos en el inspector
Fig. 9: Usando valores negativos se puede invertir la rotación del conjunto de engranajes.
Conclusión
Se puede rotar objetos en Unity de manera simple utilizando el método Rotate de la clase Transform.
Si no necesitamos precisión matemática, podemos jugar con los números y conseguir distintos resultados.
Con este tipo de rotación afectamos directamente el vector de rotación de la componente Transform, esto está bien si buscamos mostrar sólo la rotación de animación. Si se necesita que la rotación produzca interacciones físicas se debe utilizar RigidBody.
En esta ocasión traigo un mix de una hora de música instrumental ambiental que sirve para estudiar o trabajar en un proyecto durante una hora.
Puedes poner esta melodía de fondo mientras haces alguna tarea, por ejemplo estudiar un idioma o hacer un trabajo. También puedes usarla para leer algún libro durante una hora.
La idea es proponerse concentración máxima por lo que dure la melodía. Al finalizar habrás dedicado una hora a tus proyectos.
Tal vez parezca poco una hora para un proyecto, pero si transformamos esta práctica en un hábito de todos los días, al cabo de unas semanas será notable el avance.
Propósito del video
Este video lo hice más que nada para mi propio uso como una forma de medir el tiempo de dedicación.
Ya sea que quiera leer un libro, estudiar, escribir, hacer modelos 3D, practicar un idioma, programar o cualquier otra tarea, me propongo concentración máxima en lo que dure este video.
Depende de la ocasión, pero en general la música instrumental me resulta apropiada para aislarme del entorno sin perder la concentración. Por eso hice que el video tenga un volumen bajo, aunque quizás demasiado.
Todos tenemos una hora al día si juntamos los ratos libres, tiempo de televisión o quizás una siesta que no era necesaria. Es cierto que muchas veces cuesta superar el cansancio luego de un día de trabajo, por lo que se necesita fuerza de voluntad para empezar.
Pero si logramos establecer el hábito de dedicar una hora al día a un proyecto personal, pronto se puede ver el avance.
Sobre las Frases
Las frases que elegí considero son de carácter positivo y motivador, que es lo que necesitamos todas las mañanas para comenzar bien el día. Con excepción de la introducción, todas las frases se atribuyen a reconocidos filósofos, escritores, artistas, científicos y mentes brillantes, así como también hay proverbios y frases de origen desconocido. No quise incluir el autor debido a posibles errores en las fuentes de información. Creo que estas frases tienen un valor de verdad y sabiduría más allá de a quién se atribuyan. Algunos nombres de autores son:
-Johann Wolfgang von Goethe
-René Descartes
-Martin Fierro
-Paulo Coehlo
-Albert Einstein
-Steve Jobs
-Charles Dickens
-Charles Chaplin
-Bill Gates
-Isaac Asimov
-Bruce Lee
-Henry Ford
-Pablo Picasso
Sobre la música
En todo momento se escucha de fondo el sonido capturado en algún lugar de la Patagonia Argentina, alejado de la ciudad. Distintas aves como gorriones, palomas y cotorras, así como también gallos y ganzos. Niños jugando, perros, quizás un ligero viento y el sonido de las hojas. En la medida de lo posible traté de suprimir el sonido de vehículos y ruidos que no procedan de la naturaleza.
Las canciones que se escuchan son un mix sacado de la biblioteca de YouTube para creadores, algunos nombres son:
La música se encuentra limitada a un tope de decibeles, de modo que se puede ajustar un determinado volumen esperando que se mantenga a ese nivel en todo momento.
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.
La interfaz gráfica de nuestro juego le permitirá al jugador configurar parámetros como resolución, volumen, cargar y guardar partidas, comenzar a jugar, ver su puntuación y demás.
En nuestro caso nos vamos a enfocar en hacer una interfaz gráfica con la funcionalidad mínima que necesitamos para el proyecto, que es en primer lugar un «Menú principal» en el que inicie el juego y desde ahí podamos comenzar la partida.
Lo segundo que necesitamos es una interfaz gráfica que se muestre mientras estamos jugando, por el momento queremos visualizar la cuenta regresiva.
Funcionalidad de la interfaz gráfica
Lo que vamos a hacer es crear un GameObject tipo Canvas que es un elemento básico en la interfaz gráfica y luego dos empty GameObjects que contendrán todos los elementos de del menú principal y del juego.
Cuando el juego comience estará activa la interfaz gráfica del menú principal, que solo nos mostrará un botón para comenzar el juego.
Al pulsar el botón haremos que se apague la interfaz gráfica del menú principal y se encienda la del juego la cual muestra el tiempo restante para completar la misión.
Cuando el juego finalice, apagaremos la interfaz gráfica del juego y volveremos a encender la del menú principal.
De esta forma no necesitamos una segunda escena separada para el juego y podemos resolver todo en una misma escena.
Procedimiento
Antes de empezar a hacer la interfaz gráfica vamos a crear un objeto prefabricado de la puerta para poder replicar fácilmente. Tomamos la escena como quedó al final del video anterior (figura 1) y eliminamos tres puertas y tres puntos de aparición.
Fig. 1: Estado de la escena al final del video anterior. Se borran tres puertas.
Tomamos el punto de aparición que nos quedó y lo hacemos hijo de la puerta.
Fig. 2: Tambien se eliminan los puntos de aparición dejando uno.
Fig. 3: Se toma el punto de aparición y se lo hace hijo de la puerta.
Luego tomamos el GameObject Gate de la jerarquía y lo arrastramos hacia la carpeta Prefabs de nuestro proyecto, con esto creamos un prefab que podemos colocar cuantas veces sea necesario en nuestro juego.
Fig. 4: Tomando el GameObject de la jerarquía creamos un Prefab.
Fig. 5: Esto permite replicar el GameObject tantas veces como se quiera.
Vamos a crear una cámara que estará inicialmente activa para cuando mostremos la interfaz gráfica del menú principal.
Esta cámara la apagaremos en el momento de iniciar el juego para que no entre en conflicto con la cámara que viene incluida en el prefab del personaje.
Fig. 6: En la jerarquía creamos una camara que estará activa en el menú principal.
Diseño de la interfaz gráfica
Ahora comenzamos a crear los elementos necesarios para la interfaz gráfica. En primer lugar un Canvas o «Lienzo» donde se colocarán los elementos.
Fig. 7: En la jerarquía creamos un GameObject tipo Canvas para la interfaz de usuario.
Como se dijo en la introducción, creamos dos Empty GameObjects para que contengan los elementos de cada pantalla.
Fig. 8: En la jerarquía creamos dos Empty GameObjects.
Vamos a nombrar a estos objetos «MainMenu» y «Game» para identificarlos fácilmente, como se observa en la figura 9.
Fig. 9: Un empty object se llama Main Menu y el otro Game.
Haciendo clic derecho en el GameObject «MenuPrincipal» creamos un botón.
Fig. 10: Para el menú principal creamos un botón para comenzar el juego.
Para observar nuestra interfaz gráfica, en la pestaña «Game» vamos a seleccionar una relación de aspecto 16:9.
Fig. 11: Ajustamos la vista del juego para que tenga una relación de aspecto de 16:9.
Dentro del botón tenemos como hijo un GameObject tipo texto, vamos a seleccionarlo para modificarlo en el inspector.
Fig. 12: Seleccionamos el GameObject text que es hijo del botón.
Escribimos «Start Game» en el campo text que se observa en la figura 13 a la derecha.
Fig. 13: En el inspector podemos configurar el texto del botón.
Utilizando las herramientas básicas podemos posicionar el botón en la pantalla.
Fig. 14: Posicionamos el botón en la pantalla.
Al entrar en el modo juego vemos que el botón tiene una animación al pulsarlo, pero por supuesto aún no realiza ninguna acción.
Fig. 15: Al probar el juego el botón tiene una animación para el pulsado pero aún no hace nada.
Vamos a darle al botón el nombre «StartButton» para identificarlo fácilmente en la jerarquía.
Fig. 16: Renombramos el botón para que sea más fácil de identificar.
Lo siguiente que hacemos es crear un GameObject tipo texto, esta vez como hijo del objeto «Game».
Fig. 17: Creamos un GameObject tipo texto para la interfaz del juego.
En el inspector escribimos cualquier valor con el formato de tiempo, solo para visualizarlo en pantalla. Posteriormente modificaremos el texto utilizando código de C#.
Fig. 18: Escribimos un valor de tiempo en el texto. Posteriormente programaremos el funcionamiento.
Ademas de escribir el texto podemos hacer otras acciones como centrar, modificar el tamaño de la letra, el tipo de fuente, color y demás.
Fig. 19: Centramos el texto utilizando las opciones en el inspector.
Elegimos un tamaño de fuente que se ajuste bien al botón.
Fig. 20: Modificamos el tamaño.
Con el GameObject «Game» seleccionado, en la parte superior del inspector tenemos un botón que permite habilitarlo y deshabilitarlo (figura 21), esto hace que se muestre o no en la interfaz gráfica.
Usaremos esta propiedad desde el script de C# para programar el comportamiento de la interfaz gráfica.
Fig. 21: Utilizando el botón de la parte superior del inspector es posible deshabilitar el GameObject.
Adaptación a distintas resoluciones de pantalla
Cómo lograr que nuestro juego se vea bien en cualquier resolución de pantalla no es un problema menor.
Hay varias formas de encarar este tema, por ejemplo podríamos crear una interfaz de usuario distinta para cada resolución, luego utilizando un script de c# podríamos detectar qué tipo de resolución tiene el dispositivo y mostrar la interfaz gráfica apropiada.
En este video usamos un componente que trae asignado el Canvas y que se llama «Canvas Scaler», escalador de lienzo.
Este componente hará más grande o más chica la interfaz gráfica en función de la resolución de pantalla. Por el momento esto nos servirá.
Fig. 22: Seleccionamos el GameObject Canvas en la jerarquía y observamos sus parámetros en el inspector.
Desplegamos el menú «UI Scale Mode» que por defecto está en «Constant Pixel Size» y seleccionamos la opción «Scale With Screen Size».
Fig. 23: En la opción UI Scale Mode se selecciona Scale With Screen Size.
Luego colocamos una resolución de referencia, en mi caso voy a usar 1920×1080, como se observa en la figura 24.
Fig. 24: Seteamos la resolución de referencia en 1920×1080.
Ahora debemos ajustar todos los elementos para que se vean bien para esa resolución de referencia.
Fig. 25: Se observa que debemos corregir el tamaño de los elementos.
Fig. 26: Utilizando la componente Rect Transform corregimos el tamaño del elemento.
Ajustar el escalador de canvas es algo que hubiese sido mejor hacer desde el principio, pero en esta serie del laberinto estamos mostrando todos los elementos que contamos para hacer un juego en Unity, no tenía sentido mostrar el escalador de canvas antes de mostrar el canvas y sus elementos primero.
Fig. 27: Ajustamos el tamaño de la fuente a gusto.
Una vez que hemos ajustado los elementos a la nueva resolución de referencia, podemos observar el comportamiento del Canvas Scaler, seleccionando Free Aspect para la relación de aspecto, usando el menú desplegable que usamos en la figura 1.
Luego haciendo la ventana más ancha o más angosta observamos en las figuras 28 y 29 que el botón se hace proporcionalmente más grande o chica.
Fig. 28: Seteamos la opción Free Aspect para la relación de aspecto. Como se hizo en la figura 11.
Fig. 29: Al cambiar el ancho de la ventana se observa que el botón cambia su tamaño proporcionalmente.
Lo último que hacemos es crear una nueva resolución para la ventana juego. FullHD 1920×1080.
Esta resolución es sólo para la visualización en el editor, no tiene que ver con la resolución que tendrá el juego ejecutable.
Fig. 30: Agregamos una resolución de 1920×1080 para tener de referencia en la ventana juego.
Conclusión
En este artículo hemos repasado los puntos clave para crear una interfaz gráfica simple para el usuario, es decir los elementos mínimos necesarios que el jugador necesita ver para utilizar el juego.
El elemento básico de la interfaz gráfica en Unity es el Canvas, dentro se colocarán los demás elementos.
El «Canvas Scaler» nos provee una solución simple para la adaptación de la interfaz gráfica a distintas resoluciones.
Como el proyecto aún no ha avanzado mucho, el juego aún no cuenta con más funcionalidad que colocar al personaje en el escenario. Más adelante deberemos incluir más elementos en la interfaz y probablemente crear más pantallas.
BIT proviene de «binary digit» que significa «dígito binario»
Introducción – Sistemas numéricos
Antes de entrar en tema principal del bit en la informática vamos a partir desde la matemática.
Un dígito es una cantidad de una cifra que tiene un valor y ocupa una posición en algún sistema numérico. Tomemos como ejemplo el sistema decimal que usamos en el día a día. Este sistema se compone de 10 símbolos que llamamos números (0,1,2,3,4,5,6,7,8,9). A cada uno de estos símbolos les hemos asignado un valor y dados dos números distintos podemos ordenarlos por ejemplo de menor a mayor.
Además se dice que el sistema decimal es un «sistema posicional«, es decir que un dígito adquiere valor según la posición en la que se encuentra. Esto es algo que se nos enseña a muy temprana edad, el tema de las «unidades«, «decenas» y «centenas«. Los valores 1, 10 y 100 están compuestos por el mismo dígito 1, pero no valen lo mismo porque ese dígito se encuentra en posiciones distintas. La posición en la que un dígito se encuentre tendrá un peso asociado, un 1 en tercera posición vale diez veces más que un 1 en segunda posición y cien veces más que un 1 en la primera posición.
Una vez establecido un sistema numérico posicional como el decimal podemos empezar a construir operaciones entre números como la suma, resta, multiplicación y división.
¿Existen otros sistemas numéricos aparte del decimal?
El uso del sistema decimal tiene siglos de antigüedad y se piensa que su origen radica en que los seres humanos nacemos con diez dedos. Es el sistema numérico que mayormente se utiliza en todo el mundo, sin embargo nada nos impide usar cualquier cantidad de símbolos para establecer un sistema numérico posicional, por ejemplo si en lugar de usar 10 símbolos usamos 8 tenemos el sistema numérico octal, sobre el que también se definen las operaciones matemáticas del sistema decimal.
Pensemos en el siguiente ejemplo, en el sistema decimal la operación 9+1 da como resultado 10, en donde el 1 que se encuentra en la segunda posición sabemos que tiene un valor o peso de diez. Si llevamos este mismo ejemplo al sistema octal, en el que tenemos los números (0,1,2,3,4,5,6,7), la operación 7+1 da como resultado 10, en donde el 1 que se encuentra en la segunda posición tiene un valor o peso de ocho.
Otro sistema conocido es el sistema hexadecimal que tiene 16 símbolos (0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F), en este caso la operación F+1 da como resultado 10, en donde el 1 que está en segunda posición tiene un valor de 16.
Otro sistema numérico puede ser el sistema binario que es el que nos interesa en este artículo, el sistema binario está compuesto por dos símbolos, el cero y el uno, en este sistema la operación 1+1 da como resultado 10, en la cual el 1 que está en segunda posición tiene un valor de 2. Sobre el sistema binario también se pueden realizar las operaciones de suma, resta, multiplicación y división, pero también se ha desarrollado el Álgebra de Boole que define operaciones lógicas entre números binarios como las operaciones CONJUNCIÓN (AND) y la DISYUNCIÓN (OR). Y por fin llegamos al objetivo de este artículo, reiterando la frase inicial:
Un «BIT» es un dígito del sistema binario.
¿Qué es un Bit en informática?
En informática el «bit» es la unidad básica de la información, que nos permite discernir entre dos estados.
Los bits se agrupan formando palabras binarias. Una palabra binaria de 8 bits se conoce como Byte y nos permite representar 256 estados distintos.
Los procesadores en conjunto con la memoria RAM y ROM realizan operaciones utilizando palabras binarias.
Entonces utilizando bytes (que se componen de bits) es posible representar información cada vez más compleja, no sólo numérica, sino también texto, sonidos, imágenes y realizar operaciones lógicas y matemáticas sobre esa información.
Por ejemplo utilizando 3 bytes podemos representar cualquier color en el sistema RGB, un byte para el canal rojo, otro para el verde y otro para el azul.
Otro ejemplo podría ser que utilizando un byte podemos representar un caracter, entonces una frase ocupará un espacio en la memoria proporcional a la cantidad de caracteres que la componen.
¿Cual es la diferencia entre BIT y BYTE?
Bit y Byte son dos términos que se suelen confundir o incluso usar como sinónimos, así que vamos a hacer incapié en esto. Como vimos anteriormente, un BIT es un dígito binario que puede tener un valor de cero o uno, mientras que un BYTE es un arreglo ordenado de OCHO BITS. Un bit nos permite representar dos estados diferentes mientras que un BYTE, al ser una combinación de OCHO BITS nos permite representar 256 estados diferentes, 2 elevado a la 8 combinaciones posibles.
El peso de la información
Te invito a hacer un pequeño experimento para ver el espacio que ocupa la información en las computadoras. El experimento consiste en abrir el Bloc de Notas y pegar la siguiente frase sin las comillas:
«Las letras que conforman esta frase ocupan un determinado espacio en la memoria.»
La frase anterior tiene 80 caracteres, observen el peso que tiene esta información en memoria en la siguiente imagen:
Fig. 1: Un archivo txt con 80 letras sin acento pesa exactamente 80 bytes.
Esto no siempre es tan exacto porque en un archivo de texto no se almacenan solamente los caracteres sino también puede almacenar el formato en el cual organizar la información. Además los programas pueden usar métodos de compresión para representar la misma información de forma más compacta, sin embargo en el caso del bloc de notas, el peso de la información suele ser proporcional a la cantidad de caracteres, especialmente si solo hay caracteres anglosajones.
¿Qué es un Bit a nivel de Hardware?
Se dice que en las computadores se trabaja con unos y ceros, esto es cierto a nivel teórico, pero físicamente los bits se implementan con niveles de voltaje, piensa en la lámpara de una habitación que se enciende o se apaga con un interruptor, cuando el interruptor está abierto, la electricidad no fluye y la lámpara está apagada, cuando el interruptor se cierra, la electricidad fluye y la lámpara se enciende, este ejemplo es similar a lo que ocurre dentro de los microchips digitales, en donde existen interruptores extremadamente pequeños (del tamaño de decenas de átomos y cada vez se intenta hacerlos mas pequeños) que se abren o se cierran de manera controlada, es decir impiden el paso de la electricidad o lo permiten, en otras palabras a la salida de ese interruptor podemos tener un 0 o un 1. Estos pequeños interruptores se llaman TRANSISTORES.
Los micro procesadores son sistemas formados por millones de transistores. Utilizando transistores se pueden crear compuertas lógicas, desde la compuerta mas básica que es un INVERSOR que implementa la operación lógica NOT, es decir si a la entrada tenemos un 0 a la salida vamos a tener un 1, hasta compuertas mas complejas como la XOR que dados dos bits de entrada, la salida será 1 si y solo si una de las entradas es 1. Además estas compuertas lógicas se van agrupando en sistemas digitales cada vez más complejos para formar flip flops, contadores, sumadores, registros de desplazamiento o bloques de memoria.
La memoria RAM es otro ejemplo, un bloque de memoria volátil, que almacena bits temporalmente para auxiliar al procesador en sus tareas.
El disco duro almacena bits en forma de campos magnéticos utilizando la propiedad de histéresis magnética que tienen algunos materiales, mientras que los discos de estado sólido almacenan la información en bloques de memoria flash, sin partes móviles.
Conclusión
Un bit es un dígito binario que puede valer 0 o 1, forma parte de un sistema numérico análogo al sistema decimal que usamos, el sistema binario, en el que en lugar de tener 10 número, se tiene dos.
El bit es la unidad básica de información utilizada en informática. Agrupando bits en palabras binarias se puede representar todo tipo de datos como números, texto, imágenes o sonidos, es decir la información se puede digitalizar.
El bit es un concepto que se aplica a nivel de software para analizar la información y las operaciones lógicas que se realizan. En los microprocesadores esto se traduce físicamente a niveles de voltaje o magnetización en el caso de los discos duros magnéticos.
Introducción – Preparación del proyecto
En este artículo vamos a aprender a utilizar los mensajes en consola para analizar el funcionamiento de nuestro código y eventualmente detectar y corregir bugs.
Para empezar vamos a crear un proyecto nuevo en Unity, en mi caso lo voy a llamar Debugging.
Fig. 1: Escena inicial para estudiar el método Debug.Log.
En el proceso de detección y corrección de bugs nos vamos a valer principalmente de la consola de Unity, en esa ventana se imprimirán mensajes que colocaremos estratégicamente en nuestro código.
Fig. 2: Pestaña consola en Unity 3D.
En caso de no tener activa la ventana de consola se puede abrir desde la pestaña Window como se observa en la figura 3.
Fig. 3: Ruta para abrir la consola en Unity 3D.
El siguiente paso es crear un nuevo Script C#, lo podemos hacer desde la ventana del proyecto como se observa en la figura 4. Voy a darle el nombre «Debugging».
Para que el código de nuestro Script se ejecute en general no basta con crear el Script, también tiene que estar asignado a al menos un GameObject de la jerarquía. Vamos a crear un Empty GameObject y le damos un nombre significativo.
Luego con ese GameObject seleccionado arrastramos el Script hacia el inspector para asignarlo (figura 8).
Fig. 6: Crear nuevo Empty GameObject en la jerarquía.
Fig. 7: Le damos un nombre al GameObject.
Fig. 8: Se asigna el Script creado anteriormente en el inspector.
Fig. 9: El Script Debugging ha sido asignado en el inspector.
Programación C# Unity – Debug.Log
Al abrir el Script (figura 10) vemos que hay algo de código que viene escrito por defecto. Se importan las librerías básicas y tenemos los métodos Start y Update los cuales se ejecutan automáticamente.
Vamos a escribir una instrucción en el método Start. Esta instrucción es la ejecución del método Log de la clase Debug, al cual le pasaremos el mensaje que debe imprimir.
Como se observa en la figura 11, la instrucción es:
Debug.Log(«Este mensaje aparecerá en la consola»);
Fig. 11: Escribimos la instrucción Debug.Log en el método Start.
Entramos en el modo juego para probar el Script, para esto se utiliza el botón Play del Editor (figura 12).
Fig. 12: Botón para entrar en el modo juego.
Fig. 13: En la esquina inferior izquierda del programa también aparecen los mensajes de la consola.
Se observa que el mensaje aparece en la esquina inferior izquierda del programa sin que tengamos abierta la consola.
En la figura 14 separé la ventana de la consola para visualizar mejor la información.
Fig. 14: Mensaje en consola, a la derecha aparece un número que indica la cantidad de veces que se envía el mensaje.
Veamos qué ocurre si colocamos un mensaje en el método Update.
Fig. 15: Se coloca otro mensaje en el método Update.
A la derecha del mensaje se observa un número que indica la cantidad de veces que se imprime el mensaje.
Como se observa en la figura 16 el mensaje del método Update se ha impreso 82 al momento de la captura mientras que en la figura 17 el mensaje se ha impreso 350 veces.
Fig. 16: Captura de los mensajes en los primeros segundos del juego.
Fig. 17: Captura de los mensajes unos momentos después.
Utilización de variables
Los mensajes que queremos imprimir en consola puede estar almacenados en variables y el resultado es el mismo.
Fig. 18: Se definen dos strings con los mensajes y se colocan en los métodos.
Vamos a crear algunas variables más, un entero, un boolean y un float. Estos tres tipos de variables junto con los String son los tipos básicos de variables que vamos a utilizar en cualquier proyecto.
Fig. 19: Se define una variable tipo entero, bool y float, cada una con valores.
Como se observa en la figura 19 vamos a declararlas e inicializarlas con un valor, esto lo podemos hacer en distintos puntos del Script, pero lo haremos en el inicio de la clase, antes del método Start.
Luego vamos a ejecutar tres métodos Debug.Log pasando como parámetro las variables.
Fig. 20: Se ejecuta Debug.Log con las nuevas variables.
Al entrar en el modo juego, en la consola aparecen los cuatro mensajes.
Fig. 21: En la consola se visualiza el valor de cada variable.
En versiones anteriores era necesario ejecutar el método ToString() para poder imprimir el valor de una variable que no sea un string, como se observa en la figura 22, esto se puede hacer pero ya no es necesario, se pueden pasar las varibles directamente.
Fig. 22: Ejecución de ToString() que convierte el valor de la variable en texto.
Vamos a definir 4 nuevas variables, una de cada tipo y darle valores distintos, como se ve en la figura 23.
Fig. 23: Se definen más variables para realizar operaciones.
Podemos hacer que en la consola se imprima el resultado de operaciones con variables, como se observa en la figura 24.
Si comparamos los valores de la figura 23 con las impresiones en consola que se ven en la figura 25, podemos verificar esto.
Fig. 24: En el parámetro de los métodos Debug.Log se ingresan operaciones.
Fig. 25: En la consola se imprimen los resultados de esas operaciones.
También es posible concatenar distintos tipos de variables e imprimir un mensaje.
Fig. 26: Se pueden concatenar distintos tipos de variable para formar el mensaje.
Fig. 27: Mensaje compuesto por distintos tipos de variables.
Contador de Frames
Previamente colocamos una instrucción Debug.Log en el método Update, la cual contenía un mensaje fijo.
Vamos a imprimir un mensaje que va a cambiar frame a frame.
Fig. 28: Se define un entero para contar la cantidad de frames que pasan.
Para empezar definimos un entero para contar los frames y lo inicializamos en el valor 0.
Vamos a concatenar el mensaje «Este es el frame: » (incluyendo el espacio final), con el valor del contador.
Luego de mostrar el mensaje en consola, incrementamos el contador para que en el siguiente frame indique el número de frame siguiente.
Estas dos instrucciones se observan en la figura 29.
Fig. 29: Se imprime un mensaje con la cuenta de los frames y luego se incrementa el contador.
En las figuras 30 y 31 se observa la ejecución de este código en dos momentos distintos.
Fig. 30: Mensajes en los primeros segundos de ejecución.
Fig. 31: Mensajes que aparecen momentos después.
Elementos de un Array o vector de enteros
Ahora vamos a aplicar el Método Debug.Log para analizar los elementos de un Array.
Un Array es una estructura ordenada de datos de algún tipo sobre el cual se pueden hacer operaciones. Esta estructura de datos es como el vector matemático.
En primer lugar vamos a declarar un Array de enteros, esto es similar a declarar un número entero solo que añadiendo los corchetes luego del tipo, como se observa en la figura 32.
En ese mismo punto vamos a crear el Array con los valores que se ven en la figura 32.
Fig. 32: Se define un vector de enteros con distintos valores.
La lectura del Array la vamos a hacer en el método Start, en primer lugar vamos a escribir un mensaje que indique el tamaño del vector.
Para recorrer el Array utilizamos un bucle «for» que arranca desde 0 hasta la cantidad de elementos del Array menos uno.
Dentro del bucle for vamos a imprimir un mensaje en consola usando Debug.Log y concatenando texto con los valores de la variable.
Fig. 33: En el método Start vamos a analizar el array utilizando Debug.Log.
De esta forma utilizando la consola podemos estudiar el array y sus elementos, como se observa en la figura 34.
Fig. 34: Mensajes en consola que nos dan información sobre el Array.
Imprimir el resultado de la ejecución de un Método
Si tenemos un método que devuelve una variable, podemos aplicarlo como parámetro del método Debug.Log, como se observa en la figura 35.
Se define el método «UnMetodoExcelente» el cual devuelve un string.
La ejecución de este código se observa en la figura 36.
Fig. 35: Se define un método que devuelve un string para imprimir mensaje en consola.
Fig. 36: El mensaje consiste en el string que devuelve el método.
Lo mismo podríamos hacer con un método que requiera parámetros, como se ve en la figura 37.
Fig. 37: Se añade un parámetro en el método.
Fig. 38: El mensaje depende del parámetro que le demos al método.
Debug.Log para analizar el resultado de una sentencia if
El flujo de nuestro programa puede moverse por distintos caminos en función de los datos, por ejemplo en una sentencia if, el flujo del programa tomará uno de dos caminos posibles en función de la variable booleana que se evalúe.
En la figura 39 hacemos la definición de la sentencia if, la cual ejecutará un método o el otro. Cada método imprime su mensaje característico.
Fig. 39: Se definen dos métodos, uno de ellos será llamado aleatoriamente.
En las figuras 40 y 41 vemos la ejecución en dos momentos distintos del juego.
Fig. 40: En este caso se ejecutó el método CaminoDelMal.
Fig. 41: En este caso se ejecutó el método CaminoDelBien.
Conclusión
El método Debug.Log se utiliza para escribir mensajes en la consola.
Colocando estos mensajes estratégicamente en nuestro código somos capaces de visualizar el flujo del programa, obtener información del estado de nuestras variables y objetos.
El método Debug.Log toma como parámetro una variable que puede ser en principio string, bool, int o float, los tipos básicos de variables que hemos analizado en otros artículos.
Estos parámetros pueden ser entregados de distintas formas, por ejemplo es posible crear un mensaje concatenando variables, como el resultado de un método, ingresar las variables directamente o ingresar las operaciones cuyos resultados queremos que se impriman.
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 creamos el primer Script de programación que se va a encargar de colocar al jugador en una posición aleatoria del escenario. El prefabricado que vamos a colocar es el controlador de primera persona FPSController de Standard Assets.
En el escenario habrá varias puertas de entrada y salida, de modo que elegiremos una de ellas de manera aleatoria y luego colocaremos al personaje frente a la puerta elegida.
También te dejo este video en el que hablo sobre Generación de Datos Aleatorios en Unity, está dividido en dos partes, en la primera vemos cómo generar valores enteros y reales dentro de uno o más intervalos y en la segunda parte vemos cómo generar otras estructuras de datos como Vector2, Vector3, Quaternion y Color.
Punto de aparición del personaje
Comenzamos armando con los prefabricados una escena como la que se observa en la figura 1.
Fig. 1: Disposición inicial de objetos en la escena.
Creamos un empty GameObject que vamos a usar para marcar la posición donde el jugador aparecerá al comenzar el juego.
Fig. 2: Se crea un nuevo Empty GameObject para asignar el Script.
A este objeto lo vamos a llamar «SpawnPoint».
Fig. 3: El nuevo GameObject tendrá el nombre «SpawnPoint».
En el inspector podemos elegir un ícono para poder visualizar mejor el GameObject en la escena.
Fig. 4: Podemos asignar un ícono al GameObject para visualizarlo mejor en la escena.Fig. 5: El GameObject ahora se muestra con la etiqueta seleccionada.
Vamos a crear un nuevo Tag para asignarle a este GameObject. En la parte superior del inspector tenemos un menú desplegable llamado Tag, al hacer clic nos permite elegir un Tag para asignar o crear uno nuevo con «Add Tag». Hacemos clic en esta opción.
Fig. 6: Menú para asignar un Tag existente o crear uno nuevo.
En la ventana que aparece hacemos clic en el signo más y escribimos el nombre del Tag. Es importante tener en cuenta el uso de mayúsculas.
Fig. 7: Creamos un nuevo Tag llamado SpawnPoint.
Volvemos a seleccionar el GameObject «SpawnPoint» de la jerarquía y en el menú Tag seleccionamos el Tag «SpawnPoint» que acabamos de crear.
Fig. 8: Asignamos el Tag creado al GameObject «SpawnPoint».
Creación del primer Script
Finalmente vamos a empezar a programar en C#. Para empezar necesitamos crear un Script C#, para esto hacemos clic derecho en alguna carpeta del proyecto (sugiero la carpeta Script que creamos para organizarnos), luego vamos a Create > C# Script, como se observa en la figura 9.
Fig. 9: Menú de creación, seleccionamos C# Script.
Vamos a nombrar el archivo como «GameControl» debido a que se encargará de controlar los aspectos fundamentales de nuestro juego. El archivo que se crea se puede observar en la figura 10.
Fig. 10: El nuevo Script lo llamamos GameControl sin espacios.
En programación se suele utiliza una forma de escribir como la que se ve en la figura 10, se la conoce como Camel Case y consiste en eliminar los espacios y distinguir las palabras utilizando mayúsculas.
Usamos esta notación para nombres de Scripts, variables y métodos.
Antes de comenzar a programar, vamos a seleccionar un editor de Scripts, hacemos clic en Edit > Preferences.
Fig. 11: Ventana de preferencias de Unity, seleccionamos el editor MonoDevelop.
Para la serie Mi Primer Juego en Unity usamos el editor de scripts MonoDevelop, este editor fue descontinuado, es por eso que en la Serie Fundamental de Unity se usa Visual Studio.
Fig. 12: Se puede elegir el editor que más nos guste, actualmente el editor por defecto es Visual Studio.
Al abrir el Script para editar nos encontramos con un poco de código ya escrito.
En la figura 10 las primeras 3 líneas que comienzan con la palabra «using» sirven para importar librerías que nos permitirán acceder a determinada funcionalidad.
Luego viene la definición de una clase que lleva el nombre que le dimos al Script, en este caso GameControl. Además se indica la super clase, es decir la clase de la cuál hereda su comportamiento, en este caso MonoBehaviour. Osea que todos los atributos y métodos de la clase MonoBehaviour estarán disponibles para que utilicemos.
El párrafo anterior tiene varios conceptos de programación orientada a objetos que no tiene sentido profundizar en este artículo, debido a que es un tema bastante extenso. Sin embargo considero que la mención de palabras como «clase», «atributos» o «super clase» puede despertar curiosidad y animar al lector a investigar más sobre el tema. En este momento no cuento con artículos sobre programación orientada a objetos, aunque es mi deseo escribirlos pronto.
Luego hay dos métodos definidos, el método Start y el método Update. El método Start se ejecutará automáticamente una única vez al iniciar el juego y el método Update se ejecutará automáticamente una vez en cada Frame del juego.
En la región que se encuentra entre la definición de la clase y la definición dle método Start vamos a colocar todos los atributos de la clase, es decir todos las variables y objetos que utilizaremos para que el Script cumpla su funcionalidad. Hace un tiempo escribí un artículo sobre los tipos básicos de variables que vamos a utilizar en los tutoriales.
Vamos a escribir dos líneas de código, la primera es «SerializeField» entre corchetes, esto sirve para indicarle a Unity que lo que viene a continuación es un campo que se debe serializar. Sin entrar en detalles esta línea hará que los campos (variables u objetos) privados aparezcan en el inspector.
La siguiente instrucción es «private string tag», con esto estamos declarando una variable de tipo string de nombre «tag» con visibilidad privada.
Visibilidad también es un tema de programación orientada a objetos, se utiliza para ocultar atributos o métodos y que estos no sean accesibles desde un contexto externo a la clase. Los atributos o métodos con visibilidad pública son accesibles desde otras clases.
Fig. 14: Se define una variable de tipo String llamada «tag».
Asignar Script a un GameObject
Para que el código se ejecute no basta con crear un Script, debemos asignarlo al menos a un GameObject de la jerarquía.
GameControl se va a encargar de controlar los aspectos básicos del juego. En principio podría estar asignado a cualquier GameObject que esté presente durante todo el juego, pero para una mejor organización vamos a crear un Empty GameObject que llamaremos «Control».
Fig. 15: Creamos un nuevo empty GameObject.
Con el nuevo GameObject seleccionado, arrastramos el Script GameControl al inspector para agregarlo como componente.
Fig. 16: El nuevo GameObject se llamará Control.
Fig. 17: Podemos arrastrar el Script GameControl a la jerarquía del objeto Control para asignárselo.
En la figura 18 se observa que el Script ha sigo agregado como componente al Empty GameObject Control.
Fig. 18: El Script ahora es una componente del objeto Control.
Otra forma de lograr esto es utilizando el botón Add Component y buscando el Script GameControl.
Fig. 19: Otra forma de asignar el Script es a través del botón Add Component.
La razón por la que en la figura 18 no vemos el String «tag» que definimos es porque no guardamos los cambios en el Script. En la figura 20 se ve que en la pestaña del Script aparece un círculo en lugar de una cruz, esto significa que hay cambios sin guardar, se puede guardar fácilmente presionando CTRL-S.
Es recomendable guardar con bastante frecuencia para que ante eventuales fallos no perdamos nuestro trabajo.
Fig. 20: El círculo en la pestaña indica que hay cambios sin guardar.
Luego de guardar los cambios vemos en el inspector el string «Tag». En este campo vamos a escribir «SpawnPoint» que es el nombre que le dimos al tag que asignamos al GameObject SpawnPoint (figuras 5 a 8).
Fig. 21: El string Tag serializado aparece en el inspector.
Fig. 22: Podemos escribir un valor para el String.
Colocar prefab del personaje aleatoriamente
El personaje en definitiva va a ser un GameObject, en particular el Prefab FPSController de Standard Assets. Tenemos que indicarle a Unity de algun modo cuál es el prefab que queremos poner en el escenario.
Para esto vamos a definir un GameObject privado que llamaremos «playerPrefab» y lo indicaremos como serializable para que aparezca en el inspector.
En la figura 23 se observan las dos instrucciones que permiten hacer esto.
Fig. 23: Definimos un GameObject para guardar el Prefab del personaje a instanciar.
En videos anteriores habíamos creado un Prefab del FPSController y lo habíamos colocado en Assets > Prefabs > Personaje. Este es el Prefab que vamos a colocar en el escenario.
Fig. 25: El prefab FPSController de Standard Assets será nuestro personaje.
Tomamos el Prefab FPSController y lo arrastramos al nuevo campo en el inspector, como se observa en la figura 24.
Fig. 24: Asignamos el prefab en el nuevo espacio en el inspector.
Es muy importante que conservemos la referencia del GameObject del jugador, por ejemplo para destruirlo cuando la partida se termine. Esto lo vamos a hacer definiendo un nuevo GameObject privado que llamaremos «player».
En este caso no marcaremos este campo como serializable, debido a que es para uso interno del Script GameControl.
Fig. 26: Definimos un GameObject privado para guardar la referencia del personaje que colocamos en la escena.
Luego tenemos que saber dónde es que vamos a colocar al personaje en el escenario. Para esto creamos el Empty GameObject que llamamos SpawnPoint (figuras 2 a 5). vamos a hacer que sea hijo de una puerta y crearemos un Prefab de la puerta con el objeto SpawnPoint.
De modo que cada puerta tendrá su propio SpawnPoint. Si tenemos sólo una puerta en el escenario, el personaje aparecerá en ese lugar, pero si tenemos cuatro puertas (como será en nuestro caso) el personaje podrá aparecer en cualquiera de ellas aleatoriamente.
Para empezar necesitamos la referencia de los GameObject SpawnPoint, ¿pero de cuántos?. Como no queremos limitar la funcionalidad de este Script a una cantidad fija de puertas, simplemente haremos que detecte automáticamente cuántos puntos de aparición hay y elija uno de ellos aleatoriamente con la misma probabilidad.
Vamos a definir un vector o array de GameObjects, es decir una estructura de datos que contiene varios GameObjects ordenados, los cuales podremos acceder con un valor entero que indicará su posición en el array.
Para definir este array usamos la palabra GameObject seguida de corchetes, como se ilustra en la figura 27. Como este array contendrá todos los puntos de aparición del escenario lo voy a llamar «spawnPoints» y además lo definiré como privado y un campo Serializable.
Fig. 27: Se define un Array de GameObjects para guardar la referencia de todos los puntos de aparición en la escena.
En el inspector se observa cómo es esta estructura de datos. Aparece como un menú desplegable que tiene un valor entero llamado «Size», el cual indicará el tamaño del array.
Como se observa en la figura 29, si escribimos un número en esta variable, vemos que aparece esa cantidad de campos de GameObjects.
En este punto podríamos hacer que la variable Size sea igual a 4 y asignar manualmente todos los puntos de aparición, pero no queremos estar limitados a eso, queremos lograr que cuando agreguemos o quitemos una puerta, la selección del punto de aparición se haga automáticamente.
Fig. 28: Visualización del Array en el inspector, podemos indicar el tamaño del Array.
Fig. 29: Al definir el tamaño del Array nos aparecen los campos para rellenar. Podríamos colocar manualmente los puntos de aparición.
Vamos a definir un nuevo GameObject que tendrá la referencia del punto de aparición elegido, lo llamaremos «selectedSpawnPoint» y lo haremos privado y serializable para poder observarlo en el inspector.
Fig. 30: Definimos un GameObject para guardar la referencia del punto de aparición seleccionado al azar.
Programación del comportamiento
Hasta el momento estuvimos definiendo los objetos que necesitaremos, ahora comenzaremos a escribir instrucciones que son las que harán que las cosas ocurran en nuestro juego.
En este momento no cuento con artículos sobre programación básica de C#, eso queda para más adelante.
La primera instrucción la colocaremos en el método Start y se encargará de encontrar todos los puntos de aparición que haya en el escenario y guardarlos en el array «spawnPoints».
En la figura 31 se observa que el editor nos ayuda sugiriéndonos código a medida que vamos escribiendo. Si esto no ocurre podría ser por dos motivos, la opción «code-autocomplete» está desactivada en la configuración.
El otro motivo puede ser un fallo en el editor, o que Unity no está conectado con el editor. Si esto último pasa hay tratar de solucionarlo porque no solo no tendremos la sugerencia de código sino que no tendremos análisis de sintaxis.
Fig. 31: A medida que escribimos código el editor nos va sugiriendo variables o métodos que empiecen con lo que escribimos.
Para encontrar las referencias de los puntos de aparición vamos a utilizar un método de la clase GameObject que nos permitirá encontrar objetos utilizando su tag.
En las figuras 31 y 32 vemos que hay dos variantes de este método, una nos devuelve un único GameObject (figura 31) y la otra nos devuelve un array que contiene todos los GameObjects que tienen el tag que indicamos (figura 32).
Observen cómo estos dos métodos sólo se diferencian por una letra «s».
Fig. 32: Vamos a utilizar el método FindGameObjectsWithTag de la clase GameObject.
En la figura 34 se observa que al entrar en el modo juego, nuestro array es de tamaño 1 y contiene la referencia del GameObject SpawnPoint de la jerarquía (resaltado en amarillo por el propio Unity).
Fig. 34: Al entrar en el modo juego, vemos que en el inspector está la referencia del punto de aparición.
Vamos a crear algunas copias de este GameObject con CTRL-D y entrar nuevamente en el modo juego.
Fig. 35: Se crean copias de los puntos de aparición.
Ahora podemos ver que el array es de tamaño 4 y contiene todas las referencias de los GameObjects.
Fig. 36: Al entrar nuevamente en el modo juego, ahora el Array tiene cuatro elementos y están todas las referencias.
Colocamos un SpawnPoint en cada puerta.
Fig. 37: Colocamos un punto de aparición en cada puerta.
Seleccionar un elemento aleatorio del array
Para elegir un punto de aparición aleatorio necesitamos elegir aleatoriamente un elemento del array. Esto lo haremos definiendo una variable entera de nombre «rand» que será igual a un número aleatorio entre 0 y la cantidad de elementos del array menos 1.
Esto último es así porque en C# el primer elemento del array tiene la posición 0 y el último la posición «Length-1» (tamaño del array menos 1).
Para generar un número entero aleatorio utilizamos el método Range de la clase Random e indicamos dos parámetros, el inicio del intervalo (valor que se incluye) y el final del intervalo (valor que se excluye). Como se indica en la figura 38.
Fig. 38: La segunda instrucción genera un número entero aleatorio entre 0 y la cantidad de puntos de aparición menos 1.
Noten como el rago de valores de este número se ajustará automáticamente a la cantidad de puntos de aparición que haya en la jerarquía.
El siguiente paso es obtener la referencia del punto de aparición que se encuentra en la posición que define ese entero aleatorio. Para acceder a la componente de un array utilizamos el nombre del array y entre corchetes el número entero de la posición.
El elemento del array lo asignamos al GameObject selectedSpawnPoint previamente definido. Esto y lo anterior lo podemos ver en la figura 39.
Fig. 39: En la tercer instrucción se selecciona el punto de aparición del array.
En las figuras 40 y 41 vemos dos pruebas del juego en las cuales se ha elegido un punto de aparición distinto.
Fig. 40: En esta prueba el punto de aparición es el D.
Fig. 41: En esta prueba el punto de aparición es el B.
El siguiente paso es colocar el prefab del personaje en la posición del punto de aparición seleccionado.
Para esto vamos a utilizar el método Instantiate de la clase MonoBehaviour (super clase de GameControl).
El método Instantiate tiene varias versiones, vamos a utilizar una que requiere el Prefab a instanciar, la posición en donde se va a colocar y la rotación que tendrá.
Para el prefab habíamos definido el GameObject «playerPrefab». Mientras que la posición y la rotación la leeremos del GameObject «selectedSpawnPoint».
La ejecución del método Instantiate devolverá un GameObject el cual será la referencia del GameObject creado, es decir la referencia del jugador. Vamos a guardar esta referencia en el GameObject «player», porque probablemente la usemos mas adelante.
Lo que se explicó en los últimos tres párrafos se ilustran en la última instrucción de la figura 42.
Fig. 42: La cuarta y última instrucción coloca el prefab del personaje en el punto de aparición elegido.
Como observación se puede mencionar el uso del operador punto para acceder a los atributos position y rotation, los cuales se encuentran definidos en la componente Transform.
En principio no es tan simple de entender, pero a medida que adquiramos práctica y estudiemos un poco de programación va a pasar a ser algo trivial.
En las figuras 43 y 44 se muestran dos pruebas del juego en las cuales el jugador aparece en distintas posiciones.
Fig. 43: El personaje aparece en la puerta de la esquina superior izquierda.
Fig. 44: El personaje aparece en la puerta de la esquina inferior derecha.
Modularización
Estas cuatro instrucciones que hemos creado colocan al personjate en una de las puertas aleatoriamente.
Seria genial si tuviésemos una instrucción que se llame «colocarAlPersonajeEnUnaPuertaAleatoria» y que haga todo el trabajo. Entonces podríamos poner una sola instrucción dentro de start en lugar de cuatro.
Afortunadamente podemos hacer exactamente esto definiendo un nuevo método.
Fig. 45: Se define un método privado llamado «placePlayerRandomly» que no requiere parámetros y no devuelve parámetros.
El método se va a llamar «placePlayerRandomly» (colocar personaje aleatoriamente), no va a requerir parámetros, no va a devolver datos y va a ser privado. La definición de un método con todas las características enunciadas se ilustra en la figura 45.
En los primeros dos videos de esta serie utilicé nombres en español, pero considero que es mejor acostumbrarnos a utilizar nombres en inglés, además de que me facilita el trabajo de traducción.
Fig. 46: Movemos las cuatro instrucciones al método placePlayerRandomly.
Vamos a seleccionar las cuatro instrucciones que pusimos en el método Start, las vamos a cortar y a pegar en este nuevo método, como se observa en la figura 46
Luego en el método Start hacemos la llamada al método placePlayerRandomly, como vemos en la figura 47.
Fig. 47: Hacemos la llamada a placePlayerRandomly en el método Start.
Reiniciar escena
Para probar fácilmente la función de colocar al jugador en una posición aleatoria, voy a hacer que la escena se reinicie al pulsar la tecla R.
Para esto necesitamos agregar el Namespace UnityEngine.SceneManagement, un Namespace es simplemente una colección de clases agrupadas bajo un nombre, en este caso se trata de clases relacionadas al manejo de las escenas en Unity.
Agregamos el Namespace en la línea 4 que se observa en la figura 48.
Dentro de la sentencia if ejecutamos el método estático LoadScene de la clase SceneManager y pasamos como parámetro el número cero, indicando que se cargue la escena 0. Esta escena sería la primer escena declarada en la ventana Build Settings. En nuestro caso solo tenemos una escena así que simplemente se reiniciará.
Fig. 49: En el método Update hacemos la lectura de la tecla R y si es pulsada cargamos la escena 0.
Correcciones finales
Lo último que haremos será modificar la rotación de los Empty GameObjects SpawnPoint, de tal forma que la flecha azul que indica la dirección, apunte en dirección opuesta a la puerta.
Con la tecla E seleccionamos la herramienta de rotación y manteniendo pulsada la tecla SHIFT hacemos que las modificaciones del ángulo sean con paso fijo, si no me equivoco de 15 grados. De esa forma si la el ángulo del eje Y es por ejemplo 0 grados, podemos llevarlo exactamente a 90 grados gracias a ese paso fijo.
Fig. 50: Rotación de los puntos de aparición para que el personaje aparezca como si hubiese entrada por la puerta.Fig. 51: En este caso el eje azul indica la dirección a la que aparece mirando el personaje. En este caso hay que girarlo 90 grados en sentido horario.
Para concluir hacemos una compilación. En las figuras 53 y 54 se observan dos inicios distintos del juego, en cada uno el jugador aparece en una puerta distinta.
Fig. 53: Prueba de compilación 1.
Fig. 54: Prueba de compilación 2.
Conclusión
En este artículo vimos cómo crear el primer Script C# que se encargará de colocar al personaje en una posición aleatoria del escenario.
Se analizaron todos los atributos para cumplir el objetivo y se explicó para qué se usaba cada uno.
La modularización es una herramienta útil para organizar nuestro código y hacerlo más intuitivo.
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 ver los diferentes tipos de Colliders y cómo aplicarlos a nuestro modelos 3D, además vemos cómo interactúan con la componente RigidBody. Los Colliders nos permiten, por ejemplo, evitar que un personaje atraviese objetos o paredes en Unity.
Los Colliders son componentes que podemos asignar a nuestros GameObjects y que permiten que puedan colisionar con otros GameObjects que también tienen asignado Colliders.
Son importantes para lograr colisiones en física, ya que trabajarán en conjunto con las componentes RigidBody. Además nos sirven para limitar los espacios en los que los GameObjects pueden moverse.
Tipos de Colliders en Unity
Comenzamos despejando la escena de los GameObjects que quedaron en el video anterior. Vamos a dejar el pedestal con la espada y el reloj, para poder analizar los distintos tipos de Colliders.
Fig. 1: Estado inicial de la escena para comenzar a trabajar.
La espada y el pedestal son dos objetos separados agrupados bajo un mismo padre, así que podemos seleccionar la espada y apartarla del pedestal (figuras 2 y 3).
Fig. 2: El GameObject espada se encuentra seleccionado en la jerarquía.
Fig. 3: El GameObject espada se aparta del pedestal.
Además vamos a crear un cubo y una esfera haciendo clic derecho en la jerarquía y usando el menú 3D Object. Acomodamos los objetos para que se vean como en la figura 4.
Fig. 4: Arreglo de GameObjects para analizar los distintos tipos de Colliders.
Con el cubo seleccionado, observamos sus propiedades en el inspector. Por defecto viene con varias propiedades incluidas, entre ellas se encuentra una componente Box Collider.
Fig. 5: Visualización del inspector de un GameObject tipo cubo.
Las componentes podemos eliminarlas utilizando el pequeño engranaje de la parte superior derecha de una componente y haciendo clic en Remove Component, como se observa en la figura 6.
Fig. 6: Haciendo clic en el engranaje de la esquina superior derecha podemos eliminar un componente.
En la siguiente figura vemos que la componente Box Collider se ha eliminado.
Fig. 7: El Box Collider ha sido eliminado de las componentes.
Podemos agregar nuevas componentes a nuestros GameObjects haciendo clic en el botón «Add Component», al hacerlo se nos despliega una ventana con todas las componentes y que cuenta con una barra de búsqueda. Si escribimos «Collider» en la barra de búsqueda podemos observar los distintos tipos de Collider que podemos añadir a nuestro GameObject.
Por supuesto hay Colliders que son más apropiados que otros para un GameObject en particular, esto va a depender de la geometría del GameObject y del uso que vayamos a darle.
Fig. 8: Al pulsar el botón Add Component nos aparece una lista con todos los componentes que podemos agregar al GameObject.
Vamos a desactivar el componente MeshRenderer del cubo para observar mejor el Collider, las componentes suelen traer una pequeña casilla a la izquierda de su nombre para activarlas o desactivarlas.
En la figura 9 observamos que el Box Collider se representa como un Cubo definido por aristas de color verde. Debido a que el GameObject es un cubo, este tipo de Collider encaja perfectamente en su geometría.
Fig. 9: Al apagar el componente Mesh Renderer del GameObject cubo podemos visualizar el Box Collider.
Ahora seleccionemos en la jerarquía el GameObject esfera y veamos sus propiedades en el inspector.
Esta vez tiene asignado un tipo de Collider esférico.
Fig. 10: Visualización del inspector de un GameObject tipo esfera.
Nuevamente desactivamos el componente Mesh Renderer para observar el Collider (figura 11). De nuevo, como se trata de un GameObject esférico el tipo de Collider esférico se ajusta perfectamente a su geometría.
Fig. 11: Al apagar el componente Mesh Renderer del GameObject esfera podemos visualizar el Sphere Collider.
Veamos que pasa con la espada, la seleccionamos en la jerarquía y observamos sus propiedades en el inspector. A diferencia de los dos GameObjects anteriores, la espada no trae asignado un Collider por defecto, debemos asignar uno manualmente como se mostró en la figura 8.
Fig. 12: Seleccionamos la espada que previamente desplazamos del pedestal.
Primero asignamos un Box Collider. En este caso como se observa en la figura 13, vemos que aparece una caja con el mínimo volumen necesario para contener totalmente el GameObject espada.
Fig. 13: GameObject espada con un Box Collider asignado.
Luego probamos con un tipo de Collider esférico. Como se observa en la figura 14, el Collider es una esfera con el radio mínimo tal que la esfera contenga totalmente al GameObject espada.
Fig. 14: GameObject espada con un Sphere Collider asignado.
Ahora vamos a agregarle un Mesh Collider, este tipo de Collider utilizará la geometría del objeto. En la figura 15 se observan sus propiedades en el inspector.
Fig. 15: Se ha agregado un componente Mesh Collider al GameObject espada.
Desactivamos el Mesh Renderer para observar el Collider y lo que vemos es que tiene la forma exacta de la espada, el Collider coincide con el mallado geométrico de triángulos.
Fig. 16: Al apagar el componente Mesh Renderer del GameObject espada, podemos visualizar el Mesh Collider.
Por supuesto esto conlleva mayor complejidad en los cálculos de interacciones con este tipo de Collider, entonces tenemos que decidir si es necesario usarlo, o con un Box Collider nos es suficiente. Todo dependerá del propósito.
Fig. 17: Se observa que el Mesh Collider tiene una geometría compleja, lo que se traduce en mayor tiempo de cómputos.
A continuación vamos a seleccionar el GameObject del reloj en la jerarquía y aplicarle un Mesh Collider pero esta vez haremos clic en la casilla «Convex» que se observa en las propiedades del Collider en la figura 18.
Fig. 18: Se ha añadido un Mesh Collider al GameObject Reloj y se marcó la casilla Convex.
En la figura 19 observamos que ahora el tipo de Collider respeta parcialmente la geometría del objeto. Este tipo de Collider también conlleva mayor cantidad de cálculos, pero no tanto como sería en el caso anterior, es como si tuviésemos un modelo low poly del GameObject.
Además tiene otras ventajas como poder utilizarlo en modo Trigger.
Fig. 19: Se visualiza el Mesh Collider en modo convexo.
Para entender cómo se utilizan los Colliders en modo Trigger te dejo los siguientes dos artículos que forman parte de la Serie Fundamental de Unity.
Por ahora vimos tres tipos de Colliders: Box Collider, Sphere Collider y Mesh Collider, pudiendo este último ser utilizado en modo Convexo, lo que simplifica la geometría del Collider.
Fig. 20: Resumen de tipos de Colliders.
Comportamiento físico de los Colliders.
Los Colliders se comportan como barreras que no pueden atravesarse entre si. Es decir si nuestro personaje tiene un Collider e intenta atravesar un GameObject que tiene asignado un Collider (como podría ser el cubo o un reloj), no podrá hacerlo. A menos que este Collider esté en modo Trigger.
En la figura 21 se observa que el personaje está parado sobre el reloj, el Collider esférico evita que caiga.
Fig. 21: El jugador se encuentra parado sobre el reloj. El Mesh Collider que tiene asignado evita que el jugador caiga y lo atraviese.
Ahora vamos a situar la esfera suspendida en el aire sobre el cubo (este último fue escalado).
Fig. 22: Se sitúa la esfera suspendida sobre el cubo.
Con la esfera seleccionada hacemos clic en Add Component y dentro de la categoría Physics seleccionamos el componente RigidBody.
Fig. 23: Se añade un componente RigidBody a la esfera.
En la figura 24 se observan los parámetros del componente RigidBody, podemos asignar una masa, activar o desactivar la gravedad y modificar otros parámetros relacionados a la física de un objeto.
Fig. 24: Visualización de los parámetros de la componente RigidBody en el inspector.
Al entrar en el modo juego vemos que la esfera cae como por efecto de la gravedad y colisiona con la esquina del cubo (en realidad la colisión se produce con el Collider).
El componente RigidBody le otorga un comportamiento físico a la esfera.
Fig. 25: La pelota ha caído por efecto del comportamiento gravitatorio que otorga el componente RigidBody.
Agregar Colliders a las piezas del laberinto
Vamos a ir colocando uno por uno todos los prefabricados creados en el video anterior y configurando sus Colliders para que el jugador no pueda atravesar las paredes.
Fig. 26: Abrimos la carpeta donde se encuentran los prefabricados que se hicieron en el video anterior.
Primero la pieza del callejón cerrado. Tomamos el Prefab de la carpeta del proyecto y lo arrastramos a la escena o a la jerarquía. Luego en el inspector hacemos clic en el botón Add Component y elegimos el componente Box Collider.
Fig. 27: Colocamos en la escena la pieza callejón cerrado o «Dead End».
Vemos en la figura 28 que el Collider cubre totalmente el GameObject, lo cual no nos permitirá caminar en su interior.
Fig. 28: Se ha asignado un Box Collider a la pieza.
La figura 29 muestra que en el modo juego el personaje solo puede avanzar hasta la frontera del Box Collider. Este tipo de Collider no es apropiado para esta pieza.
Fig. 29: El jugador no puede ingresar al espacio entre las paredes debido a que es obstaculizado por el Box Collider.
Tenemos una alternativa, si hacemos clic en el botón «Edit Collider» del Box Collider (figura 30), podremos modificar la posición de sus caras.
Fig. 30: Utilizando el botón Edit Collider podremos editar las dimensiones del Collider.
De esta forma podremos hacer que el Box Collider encierre una parte del objeto.
Fig. 31: Se mueve una de las caras del Collider para que coincida con una de las paredes.
Luego añadiendo más Colliders y repitiendo el proceso anterior para el resto de la geometría del objeto, lograríamos representar con suficiente precisión la frontera del objeto.
Fig. 32: Agregamos dos Box Colliders más y editamos.
En la figura 33 se observa que la superposición de los tres Box Colliders cubre toda la geometría de la pieza y permite que el jugador pueda circular por el interior.
Fig. 33: Utilizando tres Box Colliders se representa la frontea del GameObject.
Lo anterior nos sirve, pero vamos a eliminar todos los Box Colliders y agregar un Mesh Collider en su lugar, en este caso la geometría del muro e bastante simple.
Fig. 34: Con el componente Mesh Renderer desactivado se puede observar el componente Mesh Collider.
Algo muy importante cuando estamos tratando con Prefabs es aplicar los cambios que vamos haciendo, para eso utilizamos el botón Apply que se encuentra en la parte superior del inspector (si tenemos el Prefab seleccionado).
Fig. 35: Es necesario aplicar los cambios que se hacen a un Prefab.
En la figura 36 se observa el modelo del portal con un componente Mesh Collider asignado.
Fig. 36: Mesh Collider asignado a la puerta. Geometría compleja.
No se justifica utilizar un Mesh Collider para este modelo, teniendo en cuenta el uso que le vamos a dar, coloquemos mejor un Box Collider y apliquemos los cambios al Prefab.
Fig. 37: Box Collider asignado a la puerta.
Lo mismo para el modelo del pedestal, con un Box Collider nos alcanza.
Fig. 38: Box Collider asignado al pedestal con la espada.
Los relojes van a ser objetos colectables, es decir cuando el jugador pase por encima de ellos, estos desaparecerán y darán más tiempo de juego.
Teniendo en cuenta el uso que les vamos a dar, vamos a utilizar un Collider esférico, aumentando un poco el radio como se observa en la figura 39.
Fig. 39: Se asigna un Sphere Collider en modo Trigger.
Luego vamos a activar la opción «Is Trigger» de la componente Sphere Collider. Esto hará que el Collider no obstaculice al jugador y permita detectar cuando este entra en la región esférica.
Fig. 40: Activando la casilla «Is Trigger» indicamos que el Collider actuará como Trigger.
Conclusión
En este artículo estudiamos distintos tipos de Colliders y para qué puede ser apropiado cada uno.
Todo depende del propósito que tengamos para el GameObject, por ejemplo en las piezas del laberinto utilizamos Mesh Collider porque necesitamos poder caminar por las regiones interiores del modelo. En cambio en los relojes utilizamo un Collider esférico porque es una pieza que el personaje podrá agarrar.
Los Colliders interactúan entre si y son muy importantes cuando entran en juego las componentes RigidBody, las cuales le dan a los GameObject comportamiento físico.
Introducción
En esta ocasión quería compartir un pequeño experimento que hice utilizando Blender, Unity y Arduino. Se trata de la simulación de un proceso industrial utilizando modelos 3D hechos en Blender y armando una escena en Unity.
El proceso industrial que intento simular es una línea de molienda de fruta. La fruta llega a través de un circuito de agua a una noria o elevador de paletilla que eleva la fruta hacia una mesa de selección, donde las personas hacen un descarte manual de la fruta en mal estado. Luego la fruta sigue su ascenso hacia un molino donde se transforma en pulpa y se deposita en un tanque. Hasta este punto llega la simulación.
Motivación
La idea de este experimento combinando Unity y Arduino surgió porque necesitaba una idea para el trabajo final de una materia de mi carrera. La materia es PLC (controladores lógicos programables).
Quise aprovechar todo lo que he aprendido de programación y diseño 3D, diseñando una simulación en computadora para testear los programas que les cargamos al PLC.
Descripción del experimento
La idea es utilizar una placa Arduino Mega para controlar la simulación en Unity. Esto se hace utilizando comunicación RS-232. La placa Arduino está dentro de una cajita impresa en 3D con un par de Switches en la tapa.
Con el Arduino Mega se leen los estados de los switches y con esa información se arma una palabra que luego se envía por RS-232. La conexión física se realiza con el puerto USB del Arduino.
En Unity se realiza la lectura de los mensajes del puerto COM.
En el video se observa que hay problemas de sincronización. Esto se debe a que la lectura del puerto COM se hace en el método Update de una clase que hereda de MonoBehaviour. Una alternativa a esto es usar un Thread distinto para realizar la lectura.
Conclusión
Aún no hemos decidido qué vamos a hacer de trabajo final, sin embargo esta idea de un proyecto híbrido Arduino-Unity me resulta muy interesante y probablemente haga una serie de videos para el canal. Creo que podría resultar algo divertido.
ACTUALIZACIÓN IMPORTANTE
Este artículo pertenece a una serie antigua del canal, actualmente hay disponible un vídeo en el que se explica mejor cómo utilizar la función Invoke para hacer que una función se llame con delay. Si deseas ver el artículo antiguo lo encontrarás más abajo.
Tengo el vídeo perfecto para esta ocasión
Este es el vídeo vemos cómo llamar funciones con DELAY en Unity.
En este artículo vamos a ver cómo utilizar el método Invoke de la clase MonoBehaviour de Unity para ejecutar una acción con un retraso determinado. Esto nos permite por ejemplo crear mecanismos que funcionan durante un tiempo.
En el siguiente video puedes ver la información resumida de este artículo.
Procedimiento
Para analizar el método Invoke utilizaremos la estación «Invoke a Method» del GameDevLab, la cual consiste en una máquina expendedora de la nueva y refrescante GameDevCola que no sólo calma la sed sino que también cura tus heridas.
Fig. 1: Estación «Invoke a Method» del GameDevLab. Máquina expendedora de GameDevCola.
Al acercarnos a la máquina podremos interactuar con ella usando la tecla E. Al pulsarla la máquina se volverá inactiva, es decir no podremos interactuar con ella por un tiempo. Luego de unos segundos la máquina expulsará una lata de GameDevCola y segundos más tarde volverá a estar habilitada para la interacción.
Fig. 2: Script InvokeAMethod sin completar.
En el Script para completar tenemos los métodos «DeliverACola» y «PrepareMachine» que entregarán la bebida y habilitarán la máquina respectivamente.
Luego tenemos redefinido el método OnTriggerStay que nos permite chequear si el personaje se encuentra parado frente a la máquina.
Dentro de este método chequeamos si el Tag del GameObject es el del jugador, luego si la máquina está lista (boolean machineReady), le decimos al objeto UIManager que se puede interactuar y este se encarga de mostrar el mensaje en pantalla.
Luego chequeamos si se presiona la tecla E. En este punto es en el que la máquina recibe la petición de una GameDevCola. Lo primero que se hace es desactivar la máquina y luego en la región con comentarios es donde debemos completar el código que falta.
Análisis de eventos en el tiempo
Quiero hacer énfasis en esta parte porque es importante que se entienda la idea.
Cuando llamamos a un método en C# la ejecución del mismo se realiza en ese preciso instante, se resuelven todas las instrucciones incluso antes de refrescar la imagen de la pantalla.
Entonces si llamáramos directamente a los métodos en la región marcada con comentarios, lo que pasaría es que la lata sería expulsada y la máquina habilitada nuevamente en el mismo instante en el que se pulsa la tecla E. Como se ilustra en la línea de tiempo de la figura 3.
Fig. 3:Diagrama de eventos en el tiempo. Esto ocurriría si llamáramos directamente a los métodos.
En la figura 3 se observa una línea de tiempo en la que hay dos puntos distintos en el tiempo, el primer punto es el comienzo del juego. En algún momento más adelante en el tiempo se encuentra el segundo punto, en el que el jugador pide una lata, la máquina la entrega y se vuelve a habilitar, estas tres acciones en el mismo instante.
Fig. 4: Diagrama de eventos en el tiempo. Esto queremos lograr.
Observemos ahora la figura 4, se observan cuatro puntos distintos de tiempo.
Punto 1: Inicio del juego.
Punto 2: El jugador hace el pedido de la GameDevCola.
Punto 3: Luego de X milisegundos de que se hace el pedido la máquina entrega la lata.
Punto 4: Luego de Y milisegundos de que se hace la entrega de la lata la máquina se vuelve a habilitar.
Esto es lo que vamos a hacer utilizando el método Invoke de la clase MonoBehaviour.
Resolución
El método Invoke lo podemos llamar directamente sin hacer referencia a una clase.
Debemos indicar dos parámetros, el primero es un String con el nombre del método que queremos invocar, el segundo parámetro es un float para indicar la cantidad de segundos que van a pasar hasta que el método finalmente sea ejecutado.
Se debe respetar las mayúsculas y minúsculas. Si nos confundimos en este punto, puede surgir un bug difícil de rastrear. Si el método se llama «DeliverACola» y nosotros invocamos el método «DeliveraCola» lo que va a ocurrir es que se va a intentar llamar a ese método pero como no se encuentra definido, el programa seguirá adelante sin ejecutar esas instrucciones.
Fig. 5: Script InvokeAMethod completo.
En la figura 5 se muestra la resolución del ejercicio. La lata será entregada dos segundos después de que el jugador presiona la tecla E y la máquina se vuelve a habilitar cuatro segundos después.
Fig. 6: Al acercarse a la máquina, nos aparece un cartel indicando que se puede interactuar.
Fig. 7: Luego de unos segundos de interactuar con la máquina, se nos entrega una lata.
Conclusión
El método Invoke nos sirve para posponer en el tiempo la ejecución de un método. Si bien no es la única forma de lograr esto, se trata de un recurso simple, fácil de recordar y fácil de implementar.
Esta página utiliza cookies para analíticas y ads, continua navegando en él si estás de acuerdo. Ajustes de CookiesACEPTAR
Política de Privacidad y Cookies
Privacy Overview
This website uses cookies to improve your experience while you navigate through the website. Out of these cookies, the cookies that are categorized as necessary are stored on your browser as they are as essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may have an effect on your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.