#6 Programar la interfaz de usuario en Unity. [Laberinto en Primera Persona]

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.

Página principal del proyecto

Vídeo relacionado a este artículo


Descripción del objetivo

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.

La definición de los métodos se muestra en la figura 5. Para ampliar la información hice un video sobre qué es un método en programación y también escribí un artículo para esta página.

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.

Hice un artículo sobre una de estas herramientas de debugging, el método Debug.Log para imprimir datos en pantalla, haz clic en este link para leerlo es una herramienta muy útil!

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.

En el primer video de la serie fundamental de Unity explico cómo leer entradas de teclado y mouse, además la diferencia que existe entre los distintos métodos, también hay un artículo sobre el video en esta página.

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.

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