#12 Interacciones en Unity. Personaje-Puertas. [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 ver una forma de resolver las interacciones en Unity entre el jugador y objetos como la puerta o el pedestal que contiene la espada.

Para ello haremos uso del método RayCast que nos permitirá trazar rayos desde el jugador a la posición a donde está mirando y examinar contra qué chocan esos rayos.

Página principal del proyecto

Antes de leer el artículo aquí tienes el vídeo correspondiente a esta entrada. Interacciones en Unity entre el personaje y las puertas.


Actualmente he creado una mejor versión del sistema de interacción que está basada en la que hacemos aquí.

Ver el nuevo sistema de interacción

Archivos de descarga

¿Qué vamos a hacer en este artículo?

Vamos a comenzar colocando fuego sobre las antorchas de las puertas, para ello vamos a usar uno de los prefabricados de Standard Assets.

Fig. 1: Vamos a hacer que las antorchas estén encendidas.

El objetivo principal es hacer que el jugador sea capaz de interactuar con las puertas y con el pedestal, esto lo resolveremos utilizando trazado de rayos y analizar contra qué chocan esos rayos.

Fig. 2: Al acercarnos a la puerta nos informará si podemos irnos o aún debemos cumplir el objetivo.

Fig. 3: Al acercarnos el pedestal nos aparece un mensaje indicando que podemos interactuar.

Importar archivos y configuración

Comenzamos descargando y extrayendo el archivo proporcionado más arriba. En la figura 4 se observan los archivos que vienen en el paquete.

Fig. 4: Archivos que vienen en el ZIP.

Seleccionamos estos archivos y los llevamos arrastramos hacia la ventana de Unity.

Fig. 5: Llevamos los archivos extraidos a Unity.

Luego creamos todas las carpetas necesarias para organizarnos de acuerdo a nuestras necesidades.

Fig. 6: Vamos a crear una carpeta para las fuentes de texto.

Vamos a modificar el sonido de las pisadas del prefab first person controller de Standard Assets, reemplazando los sonidos con los que vienen en el paquete.

Fig. 7: Seleccionamos el prefab del personaje y vamos al final del Script donde están asignados los sonidos de pisadas.

Fig. 8: Buscamos los nuevos sonidos importados.

Los llamé de la misma forma que los sonidos de Standard Assets, pero en la figura 9 los sonidos que están aplicados son los que vienen en la descarga. Además eliminé los sonidos de salto y aterrizaje.

Fig. 9: Asignamos los nuevos sonidos y eliminamos los otros dos.

Fondo para los textos

Ahora vamos a aplicar la fuente que viene en el paquete a los textos de la interfaz gráfica. Seleccionamos el GameObject Timer que tiene asignado un componente Text, en la figura 11 vemos que la fuente asignada es Arial.

Fig. 10: Seleccionamos el gameobject Timer de la jerarquía, creado anteriormente.

Fig. 11: En el inspector vamos a modificar la fuente estándar por la que viene en el paquete.

Hacemos clic sobre el punto a la derecha de Arial para seleccionar una nueva fuente.

En mi caso voy a usar «akaPostley» que es la fuente que suelo utilizar cuando hago videillos para el canal. Pero si lo prefieren pueden buscar su propia fuente o incluso crear una personalizada.

Fig. 12: En la ventana emergente seleccionamos la fuente.

Dejo unos enlaces a páginas donde se pueden conseguir fuentes:

Da Font

1001 Fonts

Luego elegimos el color, en mi caso simplemente voy a elegir blanco.

Fig. 13: También elegimos un color para la fuente, en mi caso será blanco para que contraste con el fondo.

En la figura 14 vemos cómo se ve el timer con los cambios mencionados.

Fig. 14: Nueva fuente y color para el temporizador.

Lo siguiente que vamos a hacer es crear un fondo para el texto del temporizador. Seleccionamos el GameObject Timer y creamos un objeto imagen.

Fig. 15: Creamos una imagen para hacer el fondo del timer.

Fig. 16: Seleccionamos la imagen que importamos del zip.

Ubicamos la imagen de fondo que se observa en la figura 16, la seleccionamos y vamos a configurarla como una textura tipo Sprite.

Fig. 17: En el inspector vamos a seleccionar como tipo de textura Sprite, para que tenga transparencia.

Con esta configuración la imagen de fondo tiene transparencia. Arrastramos la textura al campo source image de la componente imagen.

Fig. 18: Ahora vemos que la imagen tiene transparencia.

Fig. 19: Aplicamos la textura a la imagen que usaremos de fondo del timer.

Modificamos el resto de parámetros a gusto, en la figura 20 se ve la configuración que elegí.

Fig. 20: Configuramos la imagen de fondo a gusto, estos son mis valores.

En la figura 21 se observa que las letras en lugar de ser blancas se ven teñidas con el color de la imagen de fondo. Esto se debe a que la imagen se está renderizando por encima del texto.

Para solucionar este problema debemos colocar la imagen de fondo más arriba en la jerarquía, en la figura 22 se ve el orden de los GameObjects y en la figura 23 el resultado del cambio.

Fig. 21: Los números no se ven blancos porque la imagen está por delante del texto.

Fig. 22: Para corregir el problema vamos a la jerarquía y cambiamos de orden los GameObjects.
Fig. 23: Ahora los números del Timer están por delante de la imagen de fondo.

Crear fuego para las antorchas

Hacemos doble clic sobre algún GameObject de la puerta en la jerarquía, con esto centraremos la cámara sobre una de ellas.

Fig. 24: Seleccionamos una de las puertas en la jerarquía.

Ubicamos el prefabricado FireMobile de Standard Assets, en la figura 25 se ve la ruta donde se encuentra.

Fig. 25: Vamos a la carpeta Particle Systems de Standard Assets y elegimos el prefab Fire Mobile.

Tomamos el prefabricado y lo arrastramos a la jerarquía como hijo de la puerta.

Fig. 26: Arrastramos el prefabricado Fire Mobile y lo hacemos hijo de la puerta.

Cuando seleccionamos el prefabricado en la jerarquía podemos ver que se simula el sistema de partículas en la ventana Scene.

Fig. 27: Así se ve el prefabricado Fire Mobile.

En la figura 28 vemos la configuración que utilicé para el sistea de partículas, básicamente disminuí un poco el tamaño.

Fig. 28: Configuramos los parámetros del sistema de partículas.

Utilizando las vistas colocamos el fuego en la punta de la antorcha.

Fig. 29: Posicionamos el fuego sobre la antorcha usando las vistas frontal y lateral.

Fig. 30: Posicionamos el fuego sobre la antorcha usando las vistas frontal y lateral.

Luego haremos dos copias del objeto, una para la antorcha que se encuentra en el lado derecho de la puerta y otro para la antorcha que se encuentra varios metros por encima de la puerta.

Fig. 31: Duplicamos el fuego y lo colocamos sobre la otra antorcha.

Fig. 32: Volvemos a duplicar el fuego y lo llevamos a la punta de la antorcha que está sobre la puerta.

El propósito de esta última antorcha es que se vea desde distintas partes del laberinto.

Fig. 33: Esta llama será visible desde distintos puntos del laberinto.

Cuando terminamos de trabajar en la puerta aplicamos los cambios para que se extiendan a las demás puertas.

Fig. 34: Volvemos a seleccionar el objeto puerta y aplicamos los cambios para que se extiendan a las demás puertas.

Programar comportamiento – Scripts

En esta ocasión vamos a crear tres Scripts que llamaremos: «Gate», «Pedestal» y «UIManager».

El Script Gate se encargará de modelar el comportamiento de la puerta, el Script Pedestal se encargará del pedestal que contiene la espada y nos permitirá removerla. Y el Script UIManager se encargará de modificar los elementos de la interfaz gráfica.

Fig. 35: Creamos tres nuevos Scripts llamados Gate, Pedestal y UIManager.

Seleccionamos una puerta y le asignamos el Script Gate, luego aplicamos los cambios para las demás puertas.

Fig. 36: El Script Gate lo asignamos al GameObject Gate y aplicamos los cambios.

Ubicamos el prefabricado del pedestal con la espada y lo seleccionamos.

Fig. 37: Seleccionamos el prefabricado del pedestal.

Asignamos el Script Pedestal a este GameObject. Si es necesario podemos bloquear la ventana inspector con el candadito que se observa en la esquina superior derecha en la figura 38. También podemos colocar el prefabricado en la jerarquía, asignarle el Script, aplicar los cambios y eliminar el GameObject de la jerarquía.

Fig. 38: Al pedestal le asignamos el Script Pedestal.

Seleccionamos el GameObject Control y le asignamos el Script UIManager.

Fig. 39: Seleccionamos el objeto control de la jerarquía.

Fig. 40: Al objeto Control le asignamos el Script UIManager.

Completar Scripts

Vamos a comenzar adaptando lo que teníamos resuelto de la interfaz gráfica al nuevo objeto UIManager que se va a encargar de controlar lo que se muestra en pantalla.

Entramos en el Script Timer y seleccionamos los métodos que se observan en la figura 41, luego vamos al Script UIManager y los pegamos.

Fig. 41: Abrimos el Script Timer, seleccionamos estas instrucciones y las cortamos.

Fig. 42: Abrimos el Script UIManager y pegamos las instrucciones cortadas del Timer.

También nos llevamos la definición de los campos necesarios, por ejemplo timerText que es la referencia del texto del temporizador.

Fig. 43: También nos llevamos de Timer los objetos necesarios.

Fig. 44: Pegamos los objetos cortados de Timer e importamos el namespace UnityEngine.UI.

Cuando colocamos el campo timerText en UIManager dejamos de tener el error en el método WriteTimer.

Fig. 45: Debemos hacer público el método WriteTimer porque lo ejecutaremos desde un contexto externo.

Ahora tenemos un error en el Script Timer porque intentamos ejecutar el método WriteTimer, el cual ya no está definido en este Script. Para resolver el problema vamos a definir un objeto tipo UIManager y encontrar la referencia en el método Start, como se observa en la figura 46.

Fig. 46: En el Script Timer definimos un objeto tipo UIManager y encontramos la referencia en el método Start.

Luego all llamado del método WriteTimer le anteponemos la referencia del objeto UIManager y el operador punto, indicando que estamos queriendo ejecutar el método WriteTimer definido en el objeto UIManager.

Fig. 47: Para ejecutar el método WriteTimer debemos anteponer la referencia del objeto UIManager.

No necesitamos que el Script Timer tenga el namespace UnityEngine.UI (previamente definido en la línea 4 del Script Timer, figura 48), así que eliminamos el «using UnityEngine.UI».

Fig. 48: Ya no se necesita el namespace UnityEngine.UI en el Script Timer. Además eliminamos los objetos innecesarios.

Cuando voy a la consola me encuentro con un error, figura 49 en rojo. Nos dice que el problema está en el Script Timer, en la línea 22.

Si observamos la figura 48 vemos que el problema está en la ejecución del método GetComponent<UIManager>, faltó abrir y cerrar paréntesis.

Fig. 49: En consola aparece un error en el Script Timer.

En la figura 50 hemos resuelto el problema.

Fig. 50: El problema era la ausencia de paréntesis en la ejecución del método GetComponent.

Lo siguiente que hacemos es asignar el texto del temporizador al campo TimerText en el inspector del GameObject Control.

Fig. 51: Asignamos el texto del timer en el UIManager.

Script GameControl

Para controlar las interacciones entre el personaje y los objetos del escenario vamos a definir algunos campos y métodos en el Script GameControl que se encargarán de hacer el trazado de rayos y analizar las colisiones.

En la figura 52 vemos los nuevos campos entre el comentario «//Video 12» y el método Start.

Fig. 52: Definimos estos campos en el Script GameControl

Vamos a definir el método CheckRaycast que ejecutaremos al final del método Update, como vemos en la figura 53.

Fig. 53: Definimos el método CheckRaycast y lo ejecutamos en Update.

Script Pedestal

En el Script Pedestal vamos a escribir lo que se ve en la figura 54.

Definiremos un GameObject Sword para la referencia de la espada que está en el pedestal y un AudioSource para que el pedestal emita sonido cuando retiramos la espada.

Luego tenemos el método público RemoveSword que ejecutaremos cuando el personaje esté lo suficientemente cerca del pedestal, esté apuntando hacia la espada y presione la tecla E.

Fig. 54: En el Script Pedestal definimos estos campos y métodos.

Seleccionamos el prefab del Pedestal y utilizando Add Component agregamos un componente AudioSource.

Fig. 55: Agregamos una fuente de audio al pedestal

Ubicamos el audio «Steel» que venía dentro del paquete y lo aplicamos al componente AudioSource del pedestal, también desmarcamos la casilla «Play On Awake» y movemos el Slider «Spacial Blend» a 3D.

Fig. 56: Seleccionamos el sonido Steel que viene en el zip descargado.

Fig. 57: Asignamos el sonido de acero en el componente AudioSource del pedestal.

Vamos a colocar el prefabricado del pedestal en la jerarquía.

Fig. 58: Colocamos el pedestal en la jerarquía.

Seleccionamos el GameObject Sword que se ve en la figura 58 (puede que tenga la nombre Espada) y lo llevamos al campo «Sword» del Script Pedestal. De esta forma cuando ejecutemos el método RemoveSword, este GameObject es el que se desactivará.

Fig. 59: Asignamos el GameObject de la espada al Script Pedestal.

Mensajes de interacciones del Jugador

Vamos al Script UIManager y definimos los campos necesarios para mostrar las posibles interacciones entre el personaje y los elementos del escenario.

Vamos a definir un GameObject que será el contenedor del mensaje, es decir cuando lo desactivemos no se mostrará en pantalla y cuando lo activemos si. Un Text que será el componente texto del mensaje.

Fig. 60: Definimos estos nuevos campos en el Script UIManager.

Definimos los métodos ShowMessage y ClearMessage, el primero tomará como parámetro un String que es el mensaje que se debe mostrar. El segundo no lleva parámetros y simplemente se encarga de ocultar el GameObject que contiene al mensaje.

Fig. 61: Definimos estos nuevos métodos en el Script UIManager.

Ahora vamos a crear el elemento de la interfaz gráfica que mostrará los mensajes de interacciones.

Comenzamos creando una imagen que actuará de fondo y luego, como hijo, un objeto texto. En la figura 62 vemos estos dos objetos llamados «MessageBackground» y «MessageText».

Fig. 62: Creamos una imagen para usar como fondo del mensaje de interacción. Luego un texto como hijo.

Configuramos todos los parámetros visuales hasta que quede como nos guste, en mi caso en la figura 63 vemos el resultado.

Fig. 63: Configuramos todo a gusto, este fue mi resultado.

Vamos al GameObject Control y arrastramos los objetos a los nuevos campos definidos en UIManager.

Fig. 64: Asignamos los nuevos objetos a la componente UIManager del objeto Control.

Luego me di cuenta de que es más directo serializar el componente Text en lugar del GameObject que contiene el Text, así que como se ve en la figura 65, comenté la línea en la que se define el GameObject «messageTextGameObject» y le agregué SerializeField al Text «messageText».

Fig. 65: Modificamos de esta forma los campos de UIManager.

Esto nos modifica los campos que se ven en el inspector, arrastramos nuevamente los objetos necesarios.

Fig. 66: Volvemos a asignar los objetos a UIManager.

También completamos los campos nuevos del componente GameControl.

La distancia mínima de interacción será 4 metros. El mensaje de interacción que se muestra cuando miramos una puerta pero aún no tenemos la espada será:

«This gate is currently locked. You have to find the sword first.»

El mensaje que nos indica que podemos interactuar dirá:

«Press E to interact.»

Fig. 67: Completamos los campos del componente GameControl.

Resolver Raycasting

Para identificar qué objetos tenemos en frente, lo que vamos a hacer es crear un rayo desde el centro de la cámara en la dirección en la que está viendo el personaje.

Este rayo puede chocar con un objeto que tenga Collider o irse al infinito. Si resulta que choca con algún objeto, vamos a fijarnos si la distancia que hay ente el personaje y el objeto es menor a la distancia de interacción. Si esto se cumple vamos a intentar obtener del objeto una componente Gate o una componente Pedestal. Si el objeto no tiene ninguna de estas dos componentes (por ejemplo un muro o el suelo), en las variables van a estar en null.

Si en cambio la variable que contiene el objeto Gate es distinta de null, quiere decir que estamos mirando a la puerta a una distancia menor que la de interacción, por lo tanto podríamos interactuar con la puerta.

Esta es la escencia de la solución escrita en la figura 68 para el método CheckRaycast.

Fig. 68: Método CheckRaycast para resolver la interacción del personaje con los objetos del escenario.

Si el personaje está mirando el pedestal a una distancia menor a la de interacción vamos a comprobar si presiona la tecla E, si esto pasa ejecutaremos el método removeSword de pedestal y pondremos en true la variable swordFound, para indicar que el personaje encontró la espada.

Como detalle final ponemos la variable swordFound en false en el método Start.

Fig. 69: En el método Start hacemos la falsa la variable swordFound.

En las figuras 70 y 71 podemos ver el resultado de lo que estuvimos haciendo a lo largo de ese artículo.

Fig. 70: Al acercarnos a la puerta nos aparece el mensaje indicado en el componente GameControl.

Fig. 71: Al acercarnos al pedestal y apuntar a la espada nos aparece el mensaje de interacción que indicamos.

Conclusión

En este artículo hemos visto cómo resolver interacciones entre el personaje y ciertos elementos del escenario.

Para esto utilizamos el método RayCast de la clase Physics, que se encarga de trazar un rayo desde donde le indiquemos y en la dirección que le demos y entrega una vairbale booleana si resulta que ese rayo ha chocado contra algo.

Cuando sabemos que el rayo ha chocado contra algo, verificamos que la distancia a ese punto sea una distancia razonable en la cual el jugador pueda interactuar con un objeto.

Luego fue necesario determinar si el objeto contra el que el rayo choca es una puerta, un pedestal u otro objeto. Esto lo logramos intentando obtener las referencias de alguno de los Scripts que nos interesan y que podrían estar asignadas al objeto contra el que el rayo chocó.

Esto es una forma que se me ocurrió en la que se puede resolver las interacciones entre el jugador y distintos elementos del escenario. Me gustaría crear una solución mejor y con más posibilidades, luego de escribir este artículo he ido pensando nuevas ideas, así que posiblemente haga otros artículos sobre esto más adelante.

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