Resumen

Controlar cuándo y cómo un enemigo ataca es fundamental para equilibrar la dificultad de cualquier videojuego. En esta sesión se implementa un intervalo de tiempo antes de que el enemigo pase al estado de ataque, utilizando el sistema de alarmas de GameMaker y validaciones que evitan que la alarma se reinicie en cada frame.

¿Por qué el enemigo no puede atacar de inmediato?

Si el enemigo dispara en el instante en que detecta al jugador dentro de su circunferencia de alcance, la animación se ejecuta sin pausa y parece una ametralladora. Para evitarlo se introduce un intervalo de tiempo mediante alarmas [01:00].

Dentro del script scr_enemy_neo_idle, cuando el jugador está dentro de la circunferencia de referencia, se asigna:

gml alarm[0] = room_speed * 3;

  • room_speed devuelve la velocidad del cuarto en frames por segundo; en este proyecto son 60 fps.
  • Multiplicar por 3 equivale a 180 frames, es decir, tres segundos reales antes de ejecutar el código de la alarma.

¿Cómo funcionan las alarmas en GameMaker?

Cada objeto dispone de hasta doce alarmas (de la 0 a la 11) [01:22]. Una alarma actúa como un temporizador descendente: recibe un valor numérico en frames, y cuando llega a cero ejecuta el bloque de código asociado al evento Alarm 0, Alarm 1, etc.

Dentro del evento Alarm 0 se coloca la transición al estado de ataque:

gml state = scr_enemy_neo_attack; image_index = 0;

  • Se cambia el state al script de ataque.
  • Se reinicia image_index a cero para que la animación arranque desde el primer frame y no se vea cortada [01:55].

¿Qué pasa si no se valida la alarma?

El script de idle se ejecuta cada frame, así que sin validación la línea alarm[0] = room_speed * 3 se reescribe sesenta veces por segundo. La alarma nunca termina porque se resetea constantemente [02:28].

La solución es un condicional que solo define la alarma cuando no está activa:

gml if (alarm[0] <= 0) { alarm[0] = room_speed * 3; }

Cuando alarm[0] ya tiene un valor positivo, el bloque se ignora y la cuenta regresiva continúa sin interrupciones.

¿Qué contiene el script de ataque?

Por ahora, scr_enemy_neo_attack únicamente asigna el sprite correspondiente [03:10]:

gml sprite_index = spr_enemy_neo_attack;

Esto cambia la apariencia visual del enemigo para indicar que está atacando. Más adelante se instanciará un proyectil dentro de esta misma lógica.

¿Cómo regresa el enemigo al estado idle?

Sin una transición de vuelta, el enemigo queda congelado en el sprite de ataque cuando el jugador se aleja. Para resolverlo se usa el evento Other → Animation End [03:48]:

gml if (state == scr_enemy_neo_attack) { state = scr_enemy_neo_idle; }

  • Cuando la animación de ataque termina, el objeto vuelve automáticamente al estado idle.
  • Al regresar a idle, la detección de distancia vuelve a evaluar si el jugador sigue dentro de la circunferencia y, de ser así, reinicia la alarma para un nuevo ataque en tres segundos.

El resultado es un ciclo limpio:

  1. El enemigo persigue al jugador.
  2. Cuando está dentro del rango, espera tres segundos.
  3. Ejecuta el ataque.
  4. Vuelve a idle y repite el proceso.

Este intervalo es la base para ecualizar la dificultad del juego: reducir el multiplicador hace al enemigo más agresivo, aumentarlo lo vuelve más pasivo [03:25]. Ajustar estos valores es una de las partes más entretenidas del diseño de un videojuego.

Si quieres experimentar, prueba cambiar room_speed * 3 por room_speed * 1 y observa cómo cambia la sensación de combate. Comparte en los comentarios qué valor te parece más equilibrado para tu proyecto.