En este tutorial aprenderemos a sacudir la cámara en nuestros juegos de dos dimensiones. Me voy a basar en el artículo de Kids Can Code que a su vez toma consejo de una conferencia sobre desarrollo de juegos donde un señor explicaba cómo puedes usar las matemáticas para mejorar la cámara de tu juego: sacudiendo, dividiendo o desplazando a tiempos diferentes la cámara.
En esta ocasión aprenderemos a sacudirla. Primero crearemos una nueva escena cuyo nodo principal será la cámara 2D. Añadan un script y empecemos con las variables:
Necesitamos una variable flotante llamada decay o “decaer” en español, estará encargada de reducir el trauma. Les recomiendo que el decay se encuentre entre el 0 y el 1, si superan esa unidad la agitación de la cámara será efímero.
Después crearemos dos variables para limitar la cantidad de pixeles que nuestra cámara puede desplazarse mientras está temblando: el max_offset será un Vector2 con la posición X y Y máxima. Le sigue la rotación máxima, variable flotante que se recomienda usar con moderación. Representa la rotación en radianes, mientras mayor sea su valor, más se desencajará la cámara.
Otra variable importante es el objetivo de la cámara. Generalmente no es necesario si estamos instanciando la cámara como hija del jugador, sin embargo hay momentos donde la cámara puede ser más independiente y tomar la posición de un nodo objetivo sin necesidad de heredar su posición como hija directa. El tipo de dato que guardará su variable “objetivo” será el de una ruta, y se lo asignaremos desde el inspector.
A la agitación que recibe la cámara se le llamará “trauma”. Desde el inicio tendrá un valor de 0. Usaremos una función para añadirle otro nivel más alto, según el conflicto del juego, claro. Recomiendo que este valor esté entre el 0.1 y el 1.0, sin superar la unidad. Más de eso puede ser demasiado, pero pueden probar. Contamos también con una variable “trauma_power”, no es más que el exponente por el cuál se eleva el trauma cuando estamos agitando la cámara. Se recomienda siempre elevar al cubo o cuadrado, es decir, tres o dos. En nuestro caso la elevaremos al cuadrado.
Leyendo este artículo de Kids Can Code descubrí que ese algoritmo de ruido que llegué a llamar Perlin Noise en otros tutoriales sobre generación aleatoria de mundos, no era el mismo. El algoritmo de Perlin Noise está patentado, por tanto los desarrolladores de Godot optaron por otro algoritmo llamado Simplex Noise u Open Simplex Noise.
Necesitamos este recurso de Simplex Noise para hacer que el movimiento de la cámara mientras se agita sea aleatorio pero sin llegar a ser tan brusco, como podría suceder si usamos métodos de aleatoriedad como rand_range. Este algoritmo de ruido genera algo así como una textura llena de puntos. Cada punto tiene un valor que varía entre el -1 y el 1.
Por último, para navegar entre el eje vertical de nuestro ruido, usaremos esta variable, sin complicarnos mucho.
Ya que tenemos nuestras variables, vamos con las funciones. Al inicio de todo habilitaremos la aleatoriedad con la palabra reservada randomize() y garantizamos que nuestro ruido siempre sea “aleatorio” cambiando la semilla en la variable “noise” que almacena al recurso OpenSimplexNoise, el algoritmo. Si usamos siempre la misma semilla, por ejemplo un doce, la textura del ruido siempre será igual en cada escenario. Además de la semilla también hay que cambiar la propiedad “period” del recurso. Por defecto esa propiedad tiene un valor de 64 y está encargada de la frecuencia en el ruido, mientras menor sea su valor respecto al 64, la distancia entre los puntos será mayor. Pueden hacer la prueba entre el valor que usaremos para este ejemplo, el 4, y su valor por defecto, 64. En este último caso la pantalla se mostrará un tanto menos vibrante.
La función que añade trauma tendrá un único parámetro que recibe la cantidad de trauma. Cada vez que llamemos a esa función, siempre que queramos sacudir la cámara, haremos que la variable trauma sea igual al valor mínimo entre el trauma más el nuevo valor y un uno. De esta forma nunca podremos introducir un trauma superior a uno. Si introducimos un trauma de dos, por hacer el ejemplo, la función “min” verá que su parámetro !.0 es menor que 2, por lo que siempre preferirá ese uno. No les recomiendo ampliar esos horizontes, porque mucho trauma puede ser caótico para cualquiera.
En la función _process comprobaremos dos cosas: primero que si la cámara tiene un objetivo concreto cambie su posición a la del mismo. Segundo que si la variable trauma ya no tiene un valor de 0, entonces empiece a restarse el resultado máximo entre la resta del trauma con la variable decay por el tiempo y el 0. Esto significa que mientras el trauma no sea 0, estaremos restándole algo así como 0.01. Cuando lleguemos a un valor negativo, la función max notará que el máximo posible es el 0 y terminaremos con la sacudida. Además de restar trauma también haremos un llamado a la función shake para que vaya sacudiendo. Terminaremos el tutorial definiendo esa función.
Primero tomaremos el trauma que introducimos y lo elevaremos al cuadrado, o lo que es lo mismo, trauma_power, usando la función pow. Primero indicamos la base de la potencia y en segundo lugar su exponente. Este resultado se guarda en la variable amount. Después cambiaremos la posición y rotación de la cámara, empezando con su propiedad de rotación: el resultado que queremos está entre la variable max_roll, amount y el ruido que se obtiene con la función get_noise_2d(). Esa función de ruido toma los puntos de la posición que le indiquemos en x y y. Para que el valor de X no sea aleatorio siempre, nos ahorramos recursos si usamos el valor de la misma propiedad de semilla que tiene esa variable de ruido, a la que dimos arriba un valor aleatorio que sólo cambiará cuando iniciamos una escena. En la posición Y sí habíamos preparado una variable que hasta ahora es 0, noise_y. A medida que hacemos temblar la cámara el valor de noise_y se sumará una unidad y siempre tendremos un ruido diferente.
Para la cantidad de pixeles que se desplaza horizontal o verticalmente la cámara, haremos algo parecido, sólo que multiplicando por el máximo horizontal y vertical, y cambiando el valor de X cuando obtenemos el ruido.
De esta forma ya tenemos la cámara preparada para sacudirse. Sólo es cuestión de elegir cuándo. En este ejemplo sacudiré la cámara cada vez que mi jugador realice un ataque, para esto instancio la escena que acabamos de crear dentro del jugador, guardo en una variable del script principal la dirección de la cámara, y desde el estado de ataque accedo a la cámara y hago un llamado a su función de trauma.
Eso es todo. Espero que este tutorial te sea de utilidad. Muchas gracias a esas personas que me apoyan en Patreon, recuerden que por esa vía pueden descargar gratis mis assets de itch.io. ¡Hasta un próximo artículo!
esta bueno el video, fijate que no se ven los “_”
Tienes razón :|. Gracias por comentar! Trataré de que no vuelva a pasar.