Godot Engine: ¿Cómo crear una Máquina de Estados? I

¡Hola compañeros! Hace ya muchas noches de insomnio que empecé con el nuevo proyecto de plataformas. Quiero anunciar que ya se encuentra casi terminado, pero hoy me gustaría mostrarles algo diferente…

He dividido cada funcionalidad del juego en un paquete, como un Asset independiente, para que ustedes puedan tomar la parte que más les interese y puedan adaptarla a sus proyectos con facilidad. El primer Asset de estos que publicaré, será la Máquina de Estados que pueden usar para los personajes jugables, ya que la implementada en los NPC será diferente, incluyendo cosas relacionadas con la IA (Inteligencia Artificial)…

Esta noche les enseñaré a elaborar una máquina de estados en Godot Engine, así estarán preparados para el momento de descargar el Asset o, simplemente, sabrán los pasos para desarrollar su propia máquina. Sin embargo, puede que esto sea algo extenso, dividiré el tutorial en dos partes.

La escena del Jugador y los Nodos necesarios

¿Ya tienen identificado a su jugador? ¿Todas las animaciones de las que dispondrá? De ser así, podemos empezar a añadir los nodos que formarán parte de la Máquina de Estados. Miren este árbol de nodos, es mi jugador:

Ustedes deben decidir que tipo de nodo será la raíz de su jugador, el mio es un KinematicBody2D, por lo tanto también hago uso de un CollisionShape2D. Para controlar las imágenes del personaje uso unas hojas de sprites, divididas de la siguiente forma, que coloco en el nodo Sprite (llamado: Images):

Para animar las hojas de Sprites es necesario que también añadan un nodo AnimationPlayer, como ven en la imagen de arriba. El nodo de “BodyPivot” es un Position2D y después de crearlo, dentro de él añado el Sprite, ya que al momento de elegir la dirección del personaje (izquierda o derecha) parece más ordenado alterar un Position2D que el Sprite (Sin embargo, pueden prescindir del Position2D y usar únicamente el Sprite).

Antes de empezar con la máquina de estados…

Supongamos que todavía no crearemos la máquina de estados. Primero “prepararemos el terreno”: vayan al AnimationPlayer y empiecen a añadir una animación para cada “Estado” de la máquina.

Esta es mi animación de “Idle”. Algo que tienen en común todas las otras animaciones, es que tienen que cambiar la Textura (Asignando la hoja indicada para el estado Idle), los Hframes (El numero de frames horizontales) y la propiedad Frame (Esta cambiara cada microsegundo, y representa la imagen actual). Cuando ya cada Frame de la hoja ocupe un microsegundo, marquen el tiempo final y coloquen que esa será la duración total de la animación. Además activen las flechas que están a la derecha del tiempo total, (como yo, que las tengo en azul) para que la animación se repita. Sólo la tienen que desactivar en animaciones de un disparo, como la de Ataque.

Ahora les dejaré una imagen con todas las animaciones que voy a usar para mi máquina de estados:

Si no sabes usar el AnimationPlayer, puedes ver este tutorial de aquí, donde enseño sobre la animación de personajes usando hojas de sprites (Estoy por actualizar el Tutorial).

Llegamos al último paso antes de crear la máquina de estados. Vamos a añadir un Script para nuestro nodo principal. Aquí el mío:

Como ahora sólo tenemos a un personaje que salta, se mueve y ataca, no necesitamos muchas líneas. Sin embargo explicaré cada sección de mi Script:

  1. Variables: ahí se definen las variables que se encargaran de representar la física y las aceleraciones, tanto para el salto como para el movimiento. Estos últimos se expresan en “float” (números con decimales), o también en números enteros. La velocidad (velocity) es la variable que representa los cálculos del M.R.U (Física: Movimiento Rectilíneo Uniforme), es Vector2 porque necesita un valor para el eje horizontal y otro para el vertical.
  2. _physics_process(delta): como sabrán, esta función se ejecuta en cada frame del juego (hay como 30 frames por segundo). Dentro de ella llamo a una función que he creado para aplicar una gravedad constante todo el tiempo. Es más fácil aplicar desde el jugador la gravedad, que crear la misma función en los estados que necesiten hacer uso de ella.
  3. _input(event): esta función se ejecuta cada vez que el jugador presiona una tecla o, en caso de los teléfonos, toca la pantalla. Recibe un parámetro llamado event, que representa el botón exacto que fue presionado o el gesto que se realizó en la pantalla. Dentro de esta función estoy comprobando que flecha fue presionada, entre la izquierda y la derecha (como esta el código ahora, cuando se presiona la “izquierda” la variable vale -1, y cuando se presiona “derecha” vale 1). Según el resultado, la dirección de la imagen cambiará (Nunca debe valer 0, por eso uso esa condición en el if. De lo contrario, la imagen desaparecería). Nota: cuando cambias por números negativos la escala de un nodo, este se voltea. Osea que en mi código lo que hago es voltear la imagen. Ustedes pueden lograr un resultado similar si usan el método “flip_h = true” de los nodos Sprite2D.
  4. apply_gravity(delta): cada vez que inicia esta función se le suma al valor anterior de la variable que controla la velocidad vertical (velocity.y) el producto de multiplicar la gravedad por el tiempo físico (delta). Esto se sumará hasta que el jugador toque el suelo (if is_on_floor()), donde la velocidad vertical vuelve a ser 0. La función move_and_slide() se usa para mover al jugador según una fórmula física que haga uso de la Velocidad. Pero también hay una alternativa que permite usar unicamente el resultado de la Distancia… Sin embargo esos son otros temas, y creo que ya sabían bien que es el move_and_slide(). Cuando el get_slide_count() no sea 0, significará que el jugador esta colisionando con algo. Entonces comprobaremos que este chocando por debajo del cuerpo para decir que la velocidad vertical vuelva a 0. Si no hacen eso, notaran que cuando chocan debajo de un bloque, el jugador se queda en el aire un tiempo.

Muy bien. Después de esto ya el siguiente paso sería añadir los nodos de la máquina, junto con el código que necesitan para funcionar. Por hoy hemos finalizado la primera etapa. ¿Qué les ha parecido? Me estaban reprochando hacer siempre un enfoque básico, redundando en muchos temas… ahora quiero ver que tal queda este “nivel” de dificultad, donde doy por hecho que ya saben de algunas cosas para seguir el artículo.