Introducción al Desarrollo de Videojuegos con GameMaker · Módulo 7: Enemigos, inteligencia artificial básica y mecánicas de juego.

Puntos de control (Checkpoints)

Clase 35 de 60 · 55 min

También disponible en: EN

El rol de los puntos de control en el diseño de videojuegos

En el desarrollo de plataformas 2D, el ritmo del juego y la curva de frustración del usuario están estrechamente ligados a la distancia entre los fallos y la reanudación de la partida. Si obligamos a un jugador a reiniciar un nivel entero de diez minutos tras cometer un pequeño error de cálculo en un salto, la experiencia se vuelve punitiva y desgastante. Los puntos de control (o checkpoints) son mecanismos de guardado temporal dentro de la memoria activa del juego que capturan el progreso alcanzado, permitiendo que el personaje reaparezca en una posición segura cercana a su último logro.

Desde una perspectiva de arquitectura en GameMaker, un punto de control no requiere necesariamente guardar archivos en el disco duro (un tema que suele reservarse para sistemas de partidas guardadas permanentes). En su forma más pura y eficiente para niveles individuales, consiste en recordar las coordenadas espaciales `(x, y)` de una zona validada dentro de la room actual, junto con el restablecimiento de ciertos valores vitales del jugador, como su velocidad residual o sus puntos de impacto. En esta lección aprenderás a construir este sistema desde cero utilizando GML Code, integrándolo de forma limpia con los sistemas de salud y colisiones desarrollados en las lecciones previas.

Estrategia técnica: ¿Dónde y cómo guardar las coordenadas?

Para que la posición de reaparición persista aun cuando la instancia de `obj_player` sea destruida, reiniciada o trasladada súbitamente, las coordenadas de guardado no deben depender exclusivamente del ciclo de vida del propio jugador. Si un jugador muere y su instancia se destruye (`instance_destroy()`), todas sus variables locales y de instancia desaparecen de la memoria de GameMaker. Existen dos vías principales para resolver esto de manera profesional en proyectos de nivel principiante e intermedio: utilizar variables globales accesibles desde cualquier entidad, o emplear un objeto controlador persistente (`obj_game_controller`).

En nuestro proyecto utilizaremos variables globales (`global.checkpoint_x` y `global.checkpoint_y`). Esta solución es directa, requiere pocas líneas de código, es muy legible para principiantes y permite que tanto los objetos de punto de control (`obj_checkpoint`) como las trampas o el propio jugador consulten y modifiquen estos valores en cualquier momento del ciclo de ejecución.

💡 Recuerda: las variables con el prefijo 'global.' pertenecen al contexto global de la máquina virtual de GameMaker. A diferencia de las variables declaradas con 'var' (temporales al bloque de código) o las variables sin prefijo (pertenecientes a la instancia actual), las globales conservan su valor entre cambios de eventos y destrucción de instancias.

Diseño del recurso visual: Creación de spr_checkpoint

Para que el jugador comprenda de manera intuitiva que ha alcanzado un lugar seguro, el punto de control debe ofrecer una respuesta visual inmediata. Crearemos un sprite representativo, como un tótem místico, una bandera o una lámpara tecnológica, que conste de al menos dos estados gráficos: inactivo y activado.

Dirígete al panel Asset Browser de GameMaker, haz clic derecho sobre la carpeta de sprites y selecciona Create -> Sprite. Asigna el nombre `spr_checkpoint`. Define un tamaño de 32x32 o 32x64 píxeles según la escala de tu personaje. Dibuja o importa dos subimágenes (fotogramas): el fotograma 0 representará el checkpoint apagado o la bandera recogida (color gris o rojo), mientras que el fotograma 1 representará el checkpoint encendido o la bandera desplegada (color verde o azul brillante). Asegúrate de establecer el punto de origen (Origin) en 'Bottom Center' (Centro Inferior), facilitando que la base del objeto encaje perfectamente con las baldosas del suelo del nivel.

Esquema conceptual de la mecánica de checkpoint mostrando la activación de la bandera, el registro de coordenadas y la reaparición segura tras caer en un obstáculo.
Figura 1: Flujo de trabajo de un punto de control: detección del contacto, registro de posición segura y reaparición tras una colisión letal. · Imagen generada con IA

Programación del objeto obj_checkpoint

Ahora crearemos el objeto interactivo en el Asset Browser: Create -> Object, nombrándolo `obj_checkpoint` y asignándole el sprite `spr_checkpoint`. Este objeto debe monitorear cuándo entra en contacto con el jugador, activar su apariencia visual, asegurarse de que no se reactive innecesariamente si ya fue activado, y opcionalmente apagar otros checkpoints anteriores si se desea un comportamiento de guardado secuencial.

Comenzaremos configurando su evento Create para inicializar su estado interno. En este evento determinaremos que la animación se congele en el fotograma inactivo mediante la variable integrada `image_speed`.

// Objeto: obj_checkpoint
// Evento: Create

// El checkpoint inicia apagado y sin reproducir animación en bucle
is_active = false;
image_speed = 0;
image_index = 0;

A continuación, gestionaremos la interacción con el jugador. Esto puede realizarse mediante un evento Collision formal con `obj_player` o comprobándolo dentro del evento Step del checkpoint mediante `place_meeting()`. El evento de colisión dedicado es sumamente limpio y eficiente para principiantes. Añade el evento Collision -> Objects -> `obj_player` dentro de `obj_checkpoint` e inserta el siguiente bloque de código:

// Objeto: obj_checkpoint
// Evento: Collision con obj_player

// Solo actuar si este checkpoint aún no ha sido activado
if (!is_active)
{
    // Desactivar visualmente cualquier otro checkpoint de la sala para mantener coherencia
    with (obj_checkpoint)
    {
        is_active = false;
        image_index = 0;
    }
    
    // Activar este checkpoint en particular
    is_active = true;
    image_index = 1;
    
    // Guardar la posición de este checkpoint como la última posición segura global
    // Usamos las coordenadas (x, y) del checkpoint, alineadas con su base
    global.checkpoint_x = x;
    global.checkpoint_y = y;
    
    // Mensaje de comprobación en consola para depuración
    show_debug_message("Punto de control activado en X: " + string(global.checkpoint_x) + ", Y: " + string(global.checkpoint_y));
}

Analicemos la instrucción `with (obj_checkpoint)`. La sentencia `with` en GML itera sobre todas las instancias existentes de ese tipo de objeto en la sala. Al ejecutarla, obligamos a que todas las banderas regresen a su fotograma inactivo (`image_index = 0`), antes de que la instancia con la que el jugador acaba de chocar marque su propia variable `is_active = true` y fije su `image_index = 1`. De esta manera, solo habrá un punto de control encendido a la vez, eliminando ambigüedades en el mundo de juego.

Inicialización global y posición segura por defecto

Un error común al implementar puntos de control es asumir que el jugador siempre alcanzará uno antes de morir. Si el jugador cae en un pozo o choca contra un enemigo en los primeros metros del nivel antes de tocar el primer `obj_checkpoint`, el motor intentará leer `global.checkpoint_x` y `global.checkpoint_y`. Si estas variables no han sido inicializadas previamente, GameMaker detendrá la ejecución del juego con un error fatal de 'variable no definida'.

Para evitar este fallo crítico, debemos inicializar las variables globales con la posición de inicio del nivel. El lugar ideal para hacerlo es en el evento Create del objeto inicializador o controlador del juego (`obj_game_controller`), o de forma transitoria en el evento Create de `obj_player` al cargar la sala:

// Objeto: obj_player
// Evento: Create (añadir al final de tus variables de movimiento existentes)

// Si las variables de checkpoint global no existen aún en la sesión, definirlas
if (!variable_global_exists("checkpoint_x"))
{
    global.checkpoint_x = x;
    global.checkpoint_y = y;
}

La función `variable_global_exists()` evalúa si una variable global con ese nombre ya fue registrada en la memoria. Si es la primera vez que arranca el nivel, GameMaker detectará que no existe y guardará la posición inicial del jugador (`x` e `y` donde colocaste la instancia en la room) como punto de retorno por defecto. Si el jugador muere tras tocar una bandera, las variables globales ya existirán con los datos actualizados y esta condición no sobreescribirá el progreso.

Mecánica de reaparición (Respawn) ante daño o caídas

El paso fundamental para cerrar el circuito del punto de control es la reaparición o *respawn*. ¿Qué ocurre cuando el personaje es derrotado por un enemigo o cae por debajo del límite inferior de la pantalla? En lugar de reiniciar toda la sala con `room_restart()`, lo cual borraría los coleccionables recogidos y resetearía todos los enemigos derrotados, podemos reubicar físicamente al jugador en las coordenadas seguras y restablecer su salud.

Veamos cómo implementar una función o método de reaparición dentro de `obj_player`. Añade el siguiente bloque en el evento Create de `obj_player`:

// Objeto: obj_player
// Evento: Create

// Método para devolver al jugador a su último checkpoint seguro
respawn = function()
{
    // Mover las coordenadas al último checkpoint guardado
    x = global.checkpoint_x;
    y = global.checkpoint_y;
    
    // Anular cualquier inercia o velocidad previa para no reaparecer deslizándose o cayendo
    hspd = 0;
    vspd = 0;
    
    // Restablecer la vida del jugador si se implementó el sistema de salud del módulo
    if (variable_instance_exists(id, "hp"))
    {
        hp = max_hp;
    }
    
    // Mensaje informativo
    show_debug_message("Jugador reaparecido en zona segura.");
};

Ahora, integraremos la llamada a esta función ante dos situaciones habituales de derrota: caer a un precipicio y quedarse sin puntos de vida en el combate. En el evento Step de `obj_player`, añadimos la verificación de límites del mapa y de salud:

// Objeto: obj_player
// Evento: Step (añadir dentro de la lógica de comprobación de estado o al final)

// 1. Detección de caída al vacío (más allá de la altura de la sala)
if (y > room_height + 64)
{
    respawn();
}

// 2. Detección de pérdida total de salud
if (variable_instance_exists(id, "hp") && hp <= 0)
{
    // Si el jugador aún tiene vidas disponibles (sistema de vidas del Módulo 7)
    if (variable_instance_exists(id, "lives_count") && lives_count > 1)
    {
        lives_count -= 1;
        respawn();
    }
    else
    {
        // Si no quedan vidas, se reinicia la partida o el nivel
        room_restart();
    }
}

Consideraciones de diseño de niveles para puntos seguros

Programar el código es solo la mitad del trabajo; la ubicación física de los puntos de control en el editor de salas (`Room Editor`) determina si el juego se percibe como una experiencia gratificante o como una trampa injusta. Un mal emplazamiento puede provocar situaciones de bloqueo insalvables (*softlocks*) o bucles de muerte instantánea.

Criterio de DiseñoPráctica IncorrectaPráctica Recomendada
Proximidad a enemigosColocar el checkpoint dentro de la ruta de patrulla de un enemigo activo.Ubicar el checkpoint en una plataforma elevada o repisa fuera del alcance de proyectiles y patrullas.
Margen con precipiciosSituar la bandera justo al borde de una caída o sobre plataformas que caen.Asegurar al menos 64 píxeles (2 baldosas estándar) de suelo plano a cada lado de la bandera.
Orientación y velocidadReaparecer con velocidad vertical acumulada cayendo a través de una colisión no sólida.Resetear `vspd = 0` y `hspd = 0` al reaparecer y verificar que la máscara de colisión esté despegada del suelo.
FrecuenciaUn solo checkpoint al inicio y otro al final de un nivel de cinco minutos.Distribuir checkpoints inmediatamente después de superar desafíos de alta dificultad técnica.

Presta especial atención a la colisión vertical: si el punto de origen de `spr_checkpoint` está en 'Bottom Center', al ubicarlo en la sala, sus coordenadas coincidirán exactamente con la línea del suelo. Al reubicar al jugador en `x = global.checkpoint_x; y = global.checkpoint_y;`, asegúrate de que el punto de origen de `spr_player` también coincida en 'Bottom Center'. Si el origen del jugador estuviese en 'Top Left' (Arriba Izquierda), al asignarle la coordenada de la bandera el personaje aparecería incrustado dentro de los bloques de tierra, provocando que el sistema de colisiones lo bloquee permanentemente.

Ejercicios prácticos

Pon a prueba tus conocimientos sobre persistencia espacial y control de flujo implementando las siguientes mejoras en tu proyecto de plataformas retro.

Resumen

  • Los puntos de control (checkpoints) evitan la frustración innecesaria del jugador al permitir la reaparición en ubicaciones seguras intermedias sin tener que reiniciar todo el nivel.
  • El uso de variables globales (`global.checkpoint_x` y `global.checkpoint_y`) es una estrategia simple y confiable para almacenar coordenadas que trascienden el ciclo de vida del personaje.
  • Es fundamental inicializar las coordenadas por defecto al comienzo del nivel (usando `variable_global_exists()`) para prevenir errores de variables indefinidas si el jugador muere antes del primer checkpoint.
  • Al reubicar al jugador mediante un método `respawn()`, se deben reiniciar sus velocidades horizontal y vertical a cero (`hspd = 0; vspd = 0`) para evitar que reaparezca cayendo o resbalando accidentalmente.
  • El diseño de niveles exige ubicar los checkpoints en zonas protegidas, con suficiente base sólida y lejos del alcance inmediato de proyectiles, enemigos o plataformas móviles inestables.

Practica lo aprendido

Ejercicio 1 · 1 punto

¿Por qué en la lección se recomienda almacenar las coordenadas de reaparición en variables globales (como `global.checkpoint_x` y `global.checkpoint_y`) en lugar de variables de instancia dentro de `obj_player`?

Practicas sin cuenta: no se guarda nada. Regístrate para guardar tu progreso.

Ejercicio 2 · 1 punto

Para evitar un error fatal por variable no definida al inicio del nivel, se evalúa si la variable ya fue creada usando la función ("checkpoint_x"). Además, en el diseño de `spr_checkpoint`, el fotograma representa el checkpoint inactivo o apagado, mientras que el fotograma corresponde al estado encendido o activado.

Practicas sin cuenta: no se guarda nada. Regístrate para guardar tu progreso.

Ejercicio 3 · 1 punto

Relaciona cada elemento de código o instrucción de GML con su función dentro del sistema de puntos de control:

  • image_speed = 0;
  • with (obj_checkpoint)
  • variable_global_exists()
  • Origin en 'Bottom Center'
Practicas sin cuenta: no se guarda nada. Regístrate para guardar tu progreso.

Ingresa para guardar tu progreso — Ingresar