Cómo desarrolló Rubber Duck Games un combate contra un jefe en Evil Wizard

Rubber Duck Games, uno de los estimados ganadores del Best in Play de la GDC 2023, nos descubre cómo crearon un combate contra un jefe convincente. En este blog invitado, los miembros del equipo Banki y Sergio Wajswol te guiarán a través de todo el viaje, desde el diseño y la creación de prototipos hasta la animación, las pruebas, el equilibrio y la finalización de los efectos visuales y el audio de Evil Wizard.
Hola, lectores. Soy Banki, diseñador y productor de juegos en Rubber Duck Games. Nuestro RPG de acción lleno de humor Mago Malvado ya está a la venta en Steam y Xbox, y quería contarte entre bastidores cómo desarrollamos un combate contra un jefe.
Evil Wizard es un juego inspirado en Metroidvania que te pone en la piel de un "jefe final" derrotado en su búsqueda de venganza contra el héroe maldito.
Esta búsqueda de venganza lleva a los jugadores a través de encantadores entornos pixel-art repletos hasta los topes de hordas de enemigos a los que tendrán que vencer para recuperar lo que es suyo. Aunque el protagonista fue una vez un poderoso mago, una batalla perdida a manos del héroe le despojó de sus poderes, y tiene que recuperarlos mientras trabaja para infiltrarse en el castillo y desatar una venganza devastadora.
El primer héroe a derrotar en el juego es Hailga, la poderosa hechicera de hielo. La utilizaremos como jefa de ejemplo para esta inmersión en profundidad.
Empecemos por el principio. Diseñar un jefe (y todos los enemigos) para Evil Wizard fue un poco complicado. Aunque los jugadores luchan contra los buenos, sabíamos que debían tener un aspecto lo bastante amenazador como para ser reconocidos como enemigos.
Para los jefes, buscamos inspiración y referencias entre héroes de juegos conocidos. Con Hailga, nos inspiramos mucho en Jaina de Warcraft, ya que ambas comparten muchas similitudes.
El primer paso fue preparar una hoja de cálculo (algo que utilizamos para cada jefe del juego) en la que detallamos los rasgos principales del personaje, su mecánica y algunas referencias.

Con la mecánica diseñada, podíamos pasar al siguiente paso: la creación de prototipos.
Es hora de presentar al equipo el diseño del nuevo jefe, así que nos reunimos y yo reviso el documento y explico algunas de mis decisiones.
Aquí retocamos un poco el diseño: en algunos casos, una mecánica podría ser demasiado difícil de desarrollar, o una animación demasiado complicada de dibujar, etc. Intentamos hacer las cosas a tiempo, así que hay algunas cosas que debemos tener en cuenta para hacer el mejor jefe que podamos en el tiempo que tenemos.
En cuanto a la creación de prototipos, esto es lo que nuestro programador jefe, Diego Ordóñez, tenía que decir: "Hailga fue nuestro primer jefe en Evil Wizard y la primera vez que programaba un jefe en mi vida. Sabía que era una tarea difícil, así que empecé haciendo lo que hace todo programador: dividir los ataques en tareas sencillas y hacerlas por separado. Este jefe tiene ataques que eran sencillos de hacer reutilizando proyectiles".
"Los ataques de bala de hielo y misil de hielo resultan muy diferentes para los jugadores: uno es sencillo de esquivar, mientras que el otro representa un desafío durante el combate", continuó. "La diferencia clave es cómo el componente de ataque genera los proyectiles de hielo y la cantidad de proyectiles que se enfrentan al jugador. Con unos sencillos ajustes, pudimos reutilizar todo un sistema para presentar dos ataques diferentes. Todo esto funcionó muy bien hasta que tuve que empezar a hacer el ataque de la ventisca".
Para el combate de Hailga, queríamos aportar algo nuevo al encuentro y pensamos que el viento podía ser interesante. Esto era muy diferente a todo lo que habíamos hecho en Evil Wizard, así que tuve que empezar de cero. La idea básica era generar viento desde una dirección aleatoria para mover al jugador hacia una pared llena de pinchos que podían causar daños. La mejor forma de contrarrestar este ataque es ponerse a cubierto, así que colocamos unas cuantas estalactitas en el campo de batalla para usarlas como bloqueadores del viento. Si los jugadores se ponen detrás de una estalactita de hielo, el viento ya no emitirá fuerza sobre ellos.
Empezamos creando un componente de ataque que se encargara de gestionar los distintos sistemas que utilizamos. Los sistemas utilizados son las estalactitas, el emisor de viento y los efectos visuales de la ventisca. Me centraré en los dos primeros, y Sergio Wajswol se ocupará de los efectos visuales más adelante.
Las estalactitas son generadas usando un círculo colisionador para obtener puntos aleatorios dentro de él, luego usando esas posiciones para crear una estalactita con un desplazamiento en el eje Y. Aplicando una coroutine, hacemos que los objetos caigan bajando el eje Y - cosas simples. Una vez que tuvimos las estalactitas engendradas, pasamos al controlador de viento. Este componente funciona como un gran ventilador con forma rectangular que gira alrededor del borde de un círculo.

Como necesitábamos que el viento soplara desde direcciones aleatorias, tuvimos que orientar el emisor hacia el centro para que todo lo que estuviera dentro de la zona se viera afectado. El controlador de viento tiene un emisor de viento que almacena la rotación, dirección y posición del viento. A medida que giramos el componente, el emisor calcula estos valores y los aplica al viento (que no es más que una dirección y una fuerza). Básicamente, un Vector3 y un float.
El viento también puede bloquearse, como ya se ha dicho, con un cortavientos. Este componente tiene un BoxCollider2D que comprueba las colisiones con el jugador. Si el jugador se acerca a un bloqueador de viento, el OnTriggerEnter2D activará el bloqueador de viento y, cuando el jugador abandone el colisionador, el OnTriggerExit2D lo desactivará. Esto se ilustra con la línea cian entre el jugador y el emisor en la imagen inferior. Mientras la línea esté verde, el jugador está protegido.

Por último, teníamos que hacer que el jugador recibiera la fuerza del viento y se moviera en la dirección del viento. Para ello, disponemos de un componente WindReceiver. Se encarga de comprobar con qué fuerza impacta el viento en el jugador y desde qué dirección. Esta información se obtiene haciendo un Raycast desde el jugador hacia el receptor de viento. La información se utiliza entonces para saber si el emisor de viento está impactando en el receptor, con cuánta fuerza y desde qué dirección. Una vez tenemos toda la información, aplicamos la fuerza del viento utilizando nuestro propio controlador de movimiento y movemos al jugador en la dirección que necesitamos.
Mientras Diego empezaba a cometer los primeros ataques, yo empecé a darle comportamiento a Hailga.
Para la IA de Evil Wizard, utilizamos un recurso muy útil llamado Behavior Designer. No puedo recomendarlo lo suficiente. Es perfecto para los diseñadores de juegos que no codifican, así que el programador puede trabajar en las mecánicas y el diseñador las pone en el árbol de comportamiento del personaje, haciendo que funcione como necesitan sin codificar. Aquí puedes aprender lo básico.
Aquí está el árbol de comportamiento de Hailga:

Es un árbol grande, pero no te asustes: es más fácil de lo que parece.
Básicamente, utilizamos algunas tareas al principio del árbol para preparar la batalla. Por ejemplo, para poner al jefe en su primera fase, reproducir la animación de introducción y restablecer algunas variables.

Estas tareas sólo se ejecutan al principio del combate, por lo que a continuación añadimos un repetidor que tendría el comportamiento real del jefe, como puedes ver en la siguiente imagen.

La prioridad del comportamiento del jefe es siempre la salud. Con él, controlamos si el jefe debe cambiar a otra fase o incluso si debe morir. Entonces, primero preguntamos si el jefe tiene más del 75% de salud. Si es así, ejecutamos las tareas de la fase uno, que son picos de hielo, misiles de hielo e invocar limos de nieve (esbirros del jefe). Cuando la salud del jefe cae por debajo del 75%, el árbol pasa al siguiente selector en la parte inferior de la imagen y ejecuta las tareas correspondientes a la segunda fase, y así sucesivamente hasta que el jefe alcanza su fase final y el personaje muere con 0 de salud.
Antes de pasar al siguiente paso, quiero mencionar la función de árbol de comportamiento externo, que es una forma genial de organizar grandes árboles de comportamiento como éste.
Ya los has visto en la imagen anterior - los iconos con tres casillas son árboles de comportamiento externos.

Piensa en los árboles de comportamiento externos como un método en tu código: llamas al método en varios lugares alrededor de toda la lógica del juego y ejecuta el mismo código en cada lugar, pero sólo tienes ese código en un lugar. Si tienes que cambiar algo en esa lógica, cambias el código de ese método y cambiará en cada lugar donde se llame al método. Aquí funciona igual. Tienes un árbol de comportamiento externo, que contiene el comportamiento para realizar una acción específica, por ejemplo "invocar limos de nieve".
Si entro en nuestro árbol de comportamiento externo, encontraré esto:

Es como un mini árbol de comportamiento que comprueba que un jefe no repita el mismo ataque demasiadas veces y que no haya ya demasiados súbditos en el campo de batalla, luego invoca a los súbditos, reproduce las líneas de voz de Hailga o termina poniendo al jefe en reposo.
Si quiero cambiar la cantidad de súbditos que pueden estar en el campo de batalla a la vez, sólo tengo que cambiarlo en la tarea "Comprobar cantidad de súbditos enemigos" dentro de este árbol de comportamiento externo, y esto funcionará para cada parte del árbol que se utiliza para invocar súbditos.
Para la creación de Hailga, nuestro artista principal, Ruben Gómez, partió de la premisa de hacer referencia a personajes existentes con la esperanza de que los jugadores los reconocieran.
En este caso, utilizamos a Jaina de Warcraft (como ya se ha mencionado) y a Elsa de Frozen como personajes de referencia.
Esto es lo que Rubén tiene que decir:
"Teniendo en cuenta la premisa, empecé con el diseño de Hailga", dijo Rubén. "Tomamos algunas características de la ropa y los peinados de cada referencia, elementos que podrían ser fácilmente reconocibles en un sprite pixelado muy pequeño, y rellenamos el resto con imaginación (nada de IA, simplemente, imaginación humana, la misma que yo utilizaba cuando era niño)."

Durante la animación necesitábamos que los movimientos fueran rápidos sin perder la suavidad entre fotogramas, así que intentamos ganar tiempo con iteraciones más rápidas para la acción de cada personaje. Siempre partimos de la versión final, recortando, reordenando, escalando y rotando cada parte del personaje para cada fotograma de la animación utilizando la interpolación más cercana con la intención de reutilizar algunos elementos y evitar el antialiasing.
Después, hicimos una prueba rápida de movimientos y ajustamos de la misma manera.

Una vez terminado el borrador de la animación, refinamos cada elemento, rellenamos espacios vacíos y añadimos detalles.
Puedes ver la versión final de cada fotograma en la siguiente imagen.

Mientras se realizaban las animaciones, procedí a probar y equilibrar el combate contra el jefe. Para ello, creamos un escenario llamado "Zona de combate", que utilizamos para probar jefes, enemigos, hechizos y mucho más. Básicamente, es una pequeña zona que tiene todas las herramientas y funciones que necesitamos para probar sin tocar el juego real.
Así se veía el jefe Hailga inicial cuando probamos el personaje en la escena Zona de combate:


Por último, cuando estuvimos contentos con su comportamiento, funcionalidad y dificultad, integramos a Hailga en su lugar en el juego real.

Cuando terminamos el comportamiento y las animaciones de Hailga, empezamos a integrar las animaciones del jefe utilizando AnimatorImporterque es una gran herramienta para integrar animaciones pixel art hechas en Aseprite. Con él, en pocos pasos lo tienes todo hecho.
Ahora es el momento de dar sabor al jefe, que es cuando nuestro artista de efectos visuales, Teky, entró en juego. Llévatelo, Teky:
¡Eh! Sergio Wajswol (a.k.a. Teky), programador y artista de efectos visuales de Evil Wizard.
Los efectos visuales de Hailga fueron todo un reto, no solo porque era el primer jefe del equipo, sino porque también fue una de mis primeras tareas en el juego. Hay varias secuencias de efectos visuales empleadas durante la lucha contra el jefe -al menos una por ataque-, pero centraré mi atención en unas pocas.
Uno de los momentos cumbre de la batalla es la última transición de fase, en la que Hailga se enfurece y lanza un rayo de hielo hacia el Mago Maligno, dejando el campo de batalla congelado.

Creo que es un efecto muy bonito para desmontar y mostrar las piezas que hicieron posible la magia. Para ello, dividimos la transición en dos piezas de efectos visuales: el haz de hielo y la congelación del suelo.
Para la primera pieza, mi trabajo empezaba una vez que Diego terminaba de programar el ataque y normalmente venía con un bonito marcador de posición (en este caso, un rectángulo alargado), además de él deseándome la mejor de las suertes. A partir de aquí, me lancé a utilizar las herramientas que sabía que ya tenía en mi caja de herramientas. Pero, como era el comienzo del desarrollo, me faltaban. En el pasado, había utilizado el componente Line Renderer de Unity para tener una línea renderizada entre dos puntos cualesquiera controlada mediante script, así que empecé con eso, y luego lo combiné con un shader básico para añadir color a los bordes y al centro de la línea.

No del todo, como se puede ver arriba. Algo que comprendí rápidamente en VFX es que (contrariamente a la filosofía clásica), "el todo es la suma de las partes". Con esto quiero decir que pueden ser necesarios varios sistemas para conseguir el efecto deseado, no sólo un sombreador o una partícula.
A continuación, necesitaba que el efecto se pareciera más a un rayo (y que fuera menos sólido), así que jugué con Shader Graph para conseguirlo. Intentaré explicarlo brevemente.
Para lograr lo que quería, el sombreador necesitaba tres partes principales. En primer lugar, utilicé una textura previamente dibujada que no era más que un degradado de espejo horizontal (puedes verla en la siguiente imagen como MainTex). Usando el nodo "Pow", podía controlar fácilmente la anchura del haz (la textura se multiplica a sí misma y, como es un gradiente, encoge el haz).

Luego, utilicé Ruido Simple animado con el nodo Tiling And Offset para dos propósitos:
Para disolver el haz en ciertas partes (siempre controlado por una variable pública)
Para modificar el UV del haz, haciéndolo parecer distorsionado

A continuación, multipliqué el haz resultante por un color HDR para controlar el resplandor emisor. Por último, tomé los bordes del rayo utilizando el nodo escalonado y los multipliqué para obtener un color diferente, dejando que el centro del rayo destacara.
Una vez que terminé, tenía algo así para jugar con diferentes colores y variables.

Fue entonces cuando empecé a sentir que llegaba a alguna parte, pero me di cuenta de que me faltaba algo: el rayo se lanzaba desde el medio de la nada.
Así que, con la ayuda de partículas, hice una esfera de hielo para que fuera el punto de fundición del rayo:

Por último, necesitábamos partículas a lo largo del haz para integrarlo en el resto de la zona.

La segunda parte es más corta, pero más complicada. Para completar el efecto principal, necesitábamos poder lerpar entre dos texturas -un suelo no congelado y otro congelado-, pero en un ángulo radial que siguiera la trayectoria y la velocidad del rayo.
Para igualar la velocidad, utilizamos secuencias de comandos regulares para controlar la velocidad de la viga con una variable de 0 a 1, es decir, cuánto debe congelarse el suelo en función de la rotación actual de la viga. Para lerpear las texturas, necesitábamos un shader corto. Echa un vistazo a cómo dividimos el shader en dos partes para obtener un gradiente radial y un lerping real.

A continuación se muestra la fórmula aplicada para calcular el gradiente radial, teniendo en cuenta la posición UV y yendo exactamente de 0 a 1. Esto sincroniza el movimiento del rayo con el valor congelado.

Así es como se ve en la escena:

Para completar la escena, lo unimos todo y conseguimos un efecto genial, que sirve tanto de transición a la fase final como de uno de los ataques de este jefe.
Cuando todo estuvo terminado, Haakon Davidsen, el compositor del juego, añadió el toque final: una música alucinante que hace que los jugadores sientan el "calor" de la batalla. Puede escucharlo aquí. Y, por supuesto, una actriz de doblaje -Breanna MacDowall- hizo un trabajo increíble poniendo voz a Hailga.
Esperamos que haya disfrutado de este blog.
Experimenta por ti mismo la lucha contra el jefe de Hailga en Evil Wizard en Steam o Xbox, y permanece atento a su lanzamiento en más plataformas. Lee más historias de Unity directamente de sus desarrolladores aquí.
