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.
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.
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.
Seleccionamos estos archivos y los llevamos arrastramos hacia la ventana de Unity.
Luego creamos todas las carpetas necesarias para organizarnos de acuerdo a nuestras necesidades.
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.
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.
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.
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.
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.
En la figura 14 vemos cómo se ve el timer con los cambios mencionados.
Lo siguiente que vamos a hacer es crear un fondo para el texto del temporizador. Seleccionamos el GameObject Timer y creamos un objeto imagen.
Ubicamos la imagen de fondo que se observa en la figura 16, la seleccionamos y vamos a configurarla como una textura tipo Sprite.
Con esta configuración la imagen de fondo tiene transparencia. Arrastramos la textura al campo source image de la componente imagen.
Modificamos el resto de parámetros a gusto, en la figura 20 se ve la configuración que elegí.
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.
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.
Ubicamos el prefabricado FireMobile de Standard Assets, en la figura 25 se ve la ruta donde se encuentra.
Tomamos el prefabricado y lo arrastramos a la jerarquía como 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.
En la figura 28 vemos la configuración que utilicé para el sistea de partículas, básicamente disminuí un poco el tamaño.
Utilizando las vistas colocamos el fuego en la punta de la antorcha.
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.
El propósito de esta última antorcha es que se vea desde distintas partes del laberinto.
Cuando terminamos de trabajar en la puerta 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.
Seleccionamos una puerta y le asignamos el Script Gate, luego aplicamos los cambios para las demás puertas.
Ubicamos el prefabricado del pedestal con la espada y lo seleccionamos.
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.
Seleccionamos el GameObject Control y 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.
También nos llevamos la definición de los campos necesarios, por ejemplo timerText que es la referencia del texto del temporizador.
Cuando colocamos el campo timerText en UIManager dejamos de tener el error en el método WriteTimer.
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.
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.
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».
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.
En la figura 50 hemos resuelto el problema.
Lo siguiente que hacemos es asignar el texto del temporizador al campo TimerText en el inspector del GameObject Control.
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.
Vamos a definir el método CheckRaycast que ejecutaremos al final del método Update, como vemos en la figura 53.
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.
Seleccionamos el prefab del Pedestal y utilizando Add Component agregamos un componente AudioSource.
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.
Vamos a colocar el prefabricado del 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á.
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.
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.
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».
Configuramos todos los parámetros visuales hasta que quede como nos guste, en mi caso en la figura 63 vemos el resultado.
Vamos al GameObject Control y arrastramos los objetos a los nuevos campos definidos en UIManager.
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».
Esto nos modifica los campos que se ven en el inspector, arrastramos nuevamente los objetos necesarios.
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.»
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.
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.
En las figuras 70 y 71 podemos ver el resultado de lo que estuvimos haciendo a lo largo de ese artículo.
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.