Persecución de enemigos con lengthdir

Resumen

Programar la persecución de un enemigo en GameMaker requiere combinar estados, medición de distancia y cálculo de dirección para que reaccione al jugador de forma natural. Aquí aprendes a configurar el estado idle para que detecte un radio de acción y persiga al personaje cuando salga de él, usando funciones nativas como distance_to_object, point_direction y lengthdir_x/y.

Cómo funciona el flujo de estados del enemigo

Antes de tocar código, conviene visualizar la lógica con un diagrama simple. Imagina al enemigo como un punto rojo rodeado por un círculo naranja que representa su radio de detección, y al jugador como un punto verde que entra y sale de esa zona.

El comportamiento se reduce a tres reglas claras:

  • Si el jugador está dentro del círculo, el enemigo entra en estado de ataque (A).
  • Si el jugador está fuera del círculo, el enemigo cambia a estado de persecución (chase, C).
  • Si el jugador vuelve a entrar al radio, el enemigo deja de perseguir y ataca.

Este flujo asegura que el enemigo solo te ataque cuando estés cerca, y mientras tanto haga lo posible por alcanzarte. Lo definimos en [01:00] usando Paint para tener una referencia visual antes de programar.

Cómo medir la distancia entre el enemigo y el jugador

En el script state_idle empezamos creando una variable que mida cuán lejos está el jugador. En [03:20] declaramos var distance, y aquí aparece un detalle clave del lenguaje: al usar var, la variable se vuelve local y solo existe dentro del scope donde se creó.

¿Qué es el scope de una variable? Es el espacio donde la variable existe y puede ser leída. Una variable local con var solo vive dentro de la función o script que la declara; fuera de ahí, no existe.

Mantener orden con los scopes evita errores difíciles de rastrear. Si más adelante intentas acceder a distance desde otro script, no la vas a encontrar, y eso es justo lo que queremos: la distancia solo importa dentro del estado idle.

Para calcularla usamos distance_to_object(obj_player), que mide la separación entre las cajas de colisión del enemigo y el jugador. Un detalle importante: esta función trabaja con el objeto, no con una instancia específica, así que si hubiera varios obj_player en pantalla, calcularía contra el más cercano según el orden de creación.

Cómo visualizar la distancia con show_debug_message

Para afinar el radio, conviene ver el valor en tiempo real. En [05:30] usamos show_debug_message(distance) y, antes de que funcione, agregamos en el evento Step del enemigo la línea script_execute(state). Sin esa línea, el script del estado nunca se ejecuta.

Con el debug activo, los números aparecen en consola: 107 cuando el jugador está cerca, 311 si se aleja, y 0 cuando se tocan los sprites. Con esa referencia decidimos que 150 pixeles es un buen radio de detección.

Cómo calcular la dirección y mover al enemigo

Una vez definido el radio, escribimos la condición if distance >= 150 para activar la persecución. Dentro de ese bloque calculamos hacia dónde moverse con point_direction(x, y, obj_player.x, obj_player.y), función que devuelve el ángulo entre dos puntos.

Con la dirección lista, el movimiento se aplica así:

  • x = x + lengthdir_x(2, dir) para el eje horizontal.
  • y = y + lengthdir_y(2, dir) para el eje vertical.
  • El primer argumento (2) actúa como velocidad; el segundo es la dirección calculada.

¿Qué hace lengthdir_x en GameMaker? Toma una distancia y un ángulo, y devuelve cuánto debe sumarse al eje X para mover al objeto en esa dirección. Junto con lengthdir_y, traduce un ángulo en desplazamiento real.

Sumar el resultado a la posición actual (x = x + ...) garantiza que cada frame el enemigo avance sin perder su ubicación previa.

Cómo voltear el sprite con sign e image_xscale

Falta un detalle visual: si el enemigo camina hacia la izquierda con su sprite mirando a la derecha, se ve raro. La solución en [10:15] es usar image_xscale = sign(lengthdir_x(2, dir)).

La función sign devuelve +1, -1 o 0 según el signo del valor que le pases. Si el enemigo se mueve a la derecha, el resultado es +1 y el sprite se ve normal; si se mueve a la izquierda, devuelve -1 y aplica un efecto espejo, exactamente como hicimos con el personaje principal.

Qué pasa cuando el jugador entra al radio

Dentro del else del condicional dejamos solo sprite_index = enemy_idle, asegurando que el enemigo se quede quieto cuando el jugador esté dentro del círculo. Si está fuera, cambia a sprite_index de movimiento y comienza a perseguir.

Al correr el juego, el comportamiento se valida: dentro del radio, el enemigo permanece en idle; en cuanto el jugador se aleja más de 150 pixeles, lo sigue sin importar la dirección, gracias al cálculo continuo de point_direction y lengthdir.

En la próxima clase programamos el estado de ataque para que el enemigo dispare bolitas de energía. Si tienes dudas sobre el manejo de scopes o el uso de lengthdir, déjalas en los comentarios.