Introducción al Desarrollo de Videojuegos con GameMaker · Módulo 10: Diseño de niveles, cámaras y progresión.
Pruebas de nivel (Playtesting) y balance
Clase 50 de 60 · 50 min
También disponible en: EN
Introducción al Playtesting: La mirada del jugador novato
A lo largo de este módulo has diseñado la arquitectura espacial de tus salas (rooms), configurado cámaras suaves y establecido la transición de datos entre niveles. Sin embargo, existe un obstáculo invisible que afecta a todo desarrollador novato: la pérdida de perspectiva. Como creador de tu videojuego, conoces de memoria cada salto milimétrico, el patrón exacto de patrulla de cada enemigo y el momento preciso en el que aparecerá una trampa. Sabes hacia dónde caminar porque tú mismo colocaste los bloques.
Este fenómeno se conoce en el diseño de juegos como la «maldición del conocimiento». Provoca que el desarrollador perciba su propio nivel como excesivamente fácil o predecible, lo que a menudo le empuja a incrementar la velocidad de los enemigos o reducir las plataformas de aterrizaje. El resultado habitual es un juego desequilibrado que resulta injusto e incomprensible para un jugador nuevo. Aquí radica la importancia vital del playtesting o prueba de juego: el proceso deliberado y sistemático de observar a personas reales interactuar con tu obra para descubrir discrepancias entre lo que intentaste comunicar y lo que el jugador realmente experimenta.
Es fundamental distinguir entre depuración técnica (debugging) y playtesting. La depuración busca fallos en el código (crashes, fugas de memoria o errores de sintaxis que abordaremos formalmente en el Módulo 12). El playtesting evalúa la experiencia humana: el ritmo, la legibilidad visual, la curva de tensión, la curva de frustración y la claridad de las intenciones de diseño. Un nivel puede no contener una sola línea de código errónea y, al mismo tiempo, ser una experiencia tediosa o desesperante.
La psicología del jugador: El canal de flujo y la frustración
En el diseño de videojuegos de plataformas 2D, el objetivo central es mantener al jugador dentro del llamado «canal de flujo» (teoría formulada por el psicólogo Mihály Csíkszentmihályi). Este estado psicológico óptimo ocurre cuando el nivel de desafío de una tarea se equilibra armónicamente con la habilidad del sujeto que la ejecuta. Si el reto es muy bajo en relación con la destreza del jugador, sobreviene el aburrimiento; si el reto supera drásticamente su capacidad o no le proporciona herramientas para aprender, se genera ansiedad, frustración y, finalmente, el abandono de la partida (el clásico «rage quit»).

La frustración en un juego de plataformas rara vez proviene de una dificultad elevada per se. Obras aclamadas como Celeste o Super Meat Boy presentan niveles de dificultad extremos, pero son celebradas por su equidad. La frustración destructiva surge de tres fallos de diseño fundamentales:
1. **Falta de información (Ceguera):** Ocurre cuando el jugador muere por elementos que no podía prever ni anticipar, como los «saltos de fe» hacia fosos invisibles o trampas que caen sin previo aviso.
2. **Inconsistencia de reglas:** Sucede si un objeto sólido unas veces detiene al personaje y otras permite que un proyectil lo atraviese, o si las cajas de colisión (hitboxes) son mayores que los sprites visibles.
3. **Castigo desproporcionado:** Obligar al usuario a repetir tres minutos de saltos triviales y lentos para volver a intentar un salto difícil de un segundo genera resentimiento y fatiga mental.
Métricas espaciales y tolerancias mecánicas
El balance no se logra adivinando, sino midiendo. Antes de colocar enemigos o fosos en una room de GameMaker, debes formalizar las «métricas maestras» de tu avatar. Estas métricas están determinadas por las variables físicas que programamos en el Módulo 4 y 5 (`vspd`, `hspd`, `grav`, `jump_speed`).
| Parámetro Físico | Variable en obj_player | Valor Típico | Capacidad Espacial Resultante (Tiles de 32x32) |
|---|---|---|---|
| Salto Máximo Vertical | jump_speed = -9; grav = 0.4; | Aprox. 101 píxeles | 3 tiles de altura de holgura segura (96 px) |
| Alcance Horizontal en Salto | walk_speed = 4; en arco completo | Aprox. 180 píxeles | 5 tiles de distancia en salto horizontal neutro |
| Tiempo de Reacción Humana | Velocidad de patrulla enemiga (1.5 px/step) | Promedio: 250 ms | Mínimo 4 tiles de visión antes del impacto |
| Margen de Aterrizaje | Ancho de plataforma base | 32 a 64 píxeles | 1 tile para saltos precisos, 2 tiles para saltos estándar |
Si diseñas un abismo que mide exactamente la distancia máxima física del personaje (por ejemplo, 5 tiles completos al píxel exacto), cualquier desviación de un solo frame por parte del jugador resultará en muerte. En un nivel estándar (no enfocado a un género de ultra-precisión extrema o Kaizo), la distancia máxima de diseño debe situarse entre el 70% y el 85% de la capacidad matemática del avatar. Dejar un margen de tolerancia permite absorber las imprecisiones humanas sin penalizaciones frustrantes.
Herramientas de diagnóstico en el motor: Telemetría básica
Para balancear objetivamente, necesitas datos. Durante las pruebas de juego no puedes confiar únicamente en la memoria de los evaluadores. Si tres personas jugaron el nivel, ¿cuántas veces murió cada una en el tramo central? ¿Cuánto tiempo tardaron en resolver la sección de las plataformas móviles?
Podemos implementar un sistema ligero de telemetría y diagnóstico en GameMaker. Crearemos un objeto gestor llamado `obj_playtest_manager` que registrará las muertes por nivel, el tiempo transcurrido y mostrará una interfaz de depuración rápida activable con una tecla durante las sesiones de prueba.
/// @description Create Event de obj_playtest_manager
// Este objeto debe ser persistente para registrar la sesión a través de rooms.
persistent = true;
// Bandera para activar/desactivar la visualización del panel de playtesting
show_playtest_overlay = true;
// Métricas registradas
session_timer = 0; // Tiempo total en frames transcurridos
room_timer = 0; // Tiempo transcurrido en la room actual
deaths_current_room = 0; // Muertes registradas en la sala actual
total_deaths = 0; // Muertes acumuladas en toda la sesión
coins_collected_room = 0; // Monedas recogidas en la sala actual
// Historial de muertes por coordenadas (para detectar cuellos de botella espaciales)
death_positions_x = [];
death_positions_y = [];En el evento Step del mismo objeto, actualizamos los contadores de tiempo y configuramos un atajo de teclado para alternar la visibilidad de los datos en pantalla sin interferir con la sesión del evaluador.
/// @description Step Event de obj_playtest_manager
// Incrementar cronómetros
session_timer++;
room_timer++;
// Alternar el panel visual de métricas pulsando la tecla F1
if (keyboard_check_pressed(vk_f1)) {
show_playtest_overlay = !show_playtest_overlay;
}
// Detectar reinicio o cambio de nivel para reiniciar los contadores locales
// (Asumimos que al cambiar de room se ejecuta un evento 'Room Start')En el evento Room Start de `obj_playtest_manager`, reiniciamos las variables locales de la room pero conservamos las métricas acumuladas de la partida global:
/// @description Other -> Room Start Event de obj_playtest_manager
room_timer = 0;
deaths_current_room = 0;
coins_collected_room = 0;
// Limpiamos los puntos de muerte específicos de la sala previa
death_positions_x = [];
death_positions_y = [];Ahora necesitamos conectar la muerte del jugador con este gestor. En el objeto `obj_player`, dentro del script o bloque donde el personaje pierde su vida (por ejemplo, al colisionar con un enemigo o caer al vacío, antes de reiniciar o reaparecer en el checkpoint), ejecutamos el siguiente registro:
/// Fragmento a incluir en la lógica de muerte de obj_player
if (instance_exists(obj_playtest_manager)) {
obj_playtest_manager.total_deaths++;
obj_playtest_manager.deaths_current_room++;
// Registrar la coordenada exacta donde ocurrió el deceso
array_push(obj_playtest_manager.death_positions_x, x);
array_push(obj_playtest_manager.death_positions_y, y);
// Imprimir mensaje directo en la consola de depuración de GameMaker
show_debug_message("PLAYTEST: Muerte registrada en Room [" + room_get_name(room) + "] Coords: (" + string(x) + ", " + string(y) + ")");
}Finalmente, para visualizar estos indicadores de manera limpia en la pantalla mientras observamos jugar a un tercero, dibujamos el reporte en el evento Draw GUI:
/// @description Draw GUI Event de obj_playtest_manager
if (!show_playtest_overlay) exit;
// Convertir frames a segundos reales (asumiendo 60 FPS en Game Speed)
var _seconds_room = floor(room_timer / 60);
var _seconds_total = floor(session_timer / 60);
// Configuración visual del panel informativo
draw_set_font(-1);
draw_set_color(c_black);
draw_set_alpha(0.65);
draw_rectangle(10, 10, 310, 140, false);
draw_set_alpha(1.0);
draw_set_color(c_lime);
draw_text(20, 20, "-- DIAGNÓSTICO DE PLAYTEST (F1) --");
draw_set_color(c_white);
draw_text(20, 42, "Sala: " + room_get_name(room));
draw_text(20, 62, "Tiempo en Sala: " + string(_seconds_room) + " s");
draw_text(20, 82, "Muertes en Sala: " + string(deaths_current_room));
draw_text(20, 102, "Muertes Totales: " + string(total_deaths));
draw_text(20, 122, "Tiempo de Sesión: " + string(_seconds_total) + " s");💡 Consejo técnico: Durante una sesión formal con evaluadores externos, mantén el panel oculto mediante la tecla F1 para no distraerlos ni condicionar su comportamiento. Deja que el sistema registre los datos internamente en la consola mediante show_debug_message().
Metodología de una sesión de Playtesting real
Conseguir que amigos, familiares o colegas prueben tu juego parece sencillo, pero obtener información útil y válida requiere seguir un protocolo estricto. La mayoría de los desarrolladores cometen el error de sentarse al lado del jugador y guiarlo: «Ahí tienes que saltar», «Cuidado que ese enemigo corre rápido», «No vayas por la izquierda». Hacer esto arruina por completo la prueba, pues invalida la capacidad del juego para comunicarse por sí mismo.
A continuación, se detallan las cuatro reglas de oro para conducir sesiones de playtesting eficaces:
1. **La ley del silencio absoluto:** No expliques cómo jugar, no digas qué botón presionar y no des pistas sobre los acertijos o saltos. Si el jugador no sabe qué hacer, es una señal inequívoca de que el diseño visual, el tutorial integrado o la composición del nivel han fallado.
2. **Observa el lenguaje no verbal:** Presta atención a las reacciones físicas. ¿Frunce el ceño? ¿Suspira profundamente? ¿Afloja los hombros de alivio al alcanzar una plataforma? Si el jugador repite un salto cinco veces con expresión neutra y a la sexta vez suelta el teclado o mira el teléfono móvil, has identificado el umbral de desconexión.
3. **La técnica del «Pensamiento en Voz Alta» (Think Aloud):** Pídele amablemente al participante que exprese en palabras lo que piensa mientras juega: «Creo que debo subir por allí», «No entiendo qué hace esa palanca», «Ese enemigo me pareció injusto porque no lo vi venir». Esto te otorga una ventana directa a su modelo mental sin necesidad de interrumpirlo.
4. **Preguntas abiertas tras la sesión:** Cuando el jugador termine (o decida abandonar), no hagas preguntas inductivas como: «¿Te pareció divertido el nivel 2?». Formula preguntas neutrales: «¿Cuál fue la parte donde sentiste mayor confusión?», «Si tuvieras que eliminar un obstáculo de la sala, ¿cuál sería?», «Describe el ritmo del personaje al moverse».
Detección de cuellos de botella y técnicas de mitigación
Un cuello de botella (chokepoint) es una sección específica del nivel donde se concentra un porcentaje anómalamente alto de las muertes o del tiempo de juego sin que forme parte del clímax deliberado de la experiencia. Si tu nivel tiene 10 retos y el 80% de las muertes de todos los evaluadores ocurren en el obstáculo número 4, ese punto está desbalanceado.
Para resolver cuellos de botella sin eliminar el desafío, los desarrolladores emplean las llamadas **Técnicas de Pulido Invisible**:
**Coyote Time (Tiempo Coyote):** Brinda al avatar una ventana de 5 a 8 frames de gracia tras abandonar una cornisa en la que aún puede ejecutar el salto. Fisiológicamente, el ojo humano percibe que el personaje sigue en la plataforma cuando el motor físico ya detectó el vacío. Sin Coyote Time, el jugador siente que el juego «se comió su comando».
**Buffer de Salto (Jump Buffering):** Si el usuario presiona el botón de saltar 5 frames antes de tocar el suelo, el motor almacena esa intención y ejecuta el salto en el frame exacto del impacto con el bloque sólido. Esto evita que los saltos rítmicos y consecutivos se sientan toscos o rígidos.
**Migajas de pan (Breadcrumbing):** Guiar la atención mediante el arte. Las monedas, antorchas de fondo o gemas coleccionables dispuestas en forma de arco sugieren la parábola exacta que debe describir el salto, comunicando el camino seguro de forma no invasiva.
Plantilla de Matriz de Observación de Playtesting
Llevar un registro ordenado te evitará reaccionar desmedidamente a la opinión aislada de un único usuario. Una regla fundamental del diseño es: *«Si un jugador te dice que algo no se siente bien, casi siempre tiene razón. Si te dice cómo solucionarlo, casi siempre se equivoca»*. Identifica los síntomas, no implementes ciegamente las sugerencias de terceros.
| Identificador de Prueba | Área / Sala | Problema Observado | Causa Raíz Identificada | Ajuste de Balance Aplicado |
|---|---|---|---|---|
| Tester_01 (Novato) | rm_level1_middle | Cae en el vacío tres veces consecutivas | Salto ciego: la cámara no muestra el suelo inferior | Se reposicionó la plataforma de destino 64 px a la izquierda y se agregó una moneda guía |
| Tester_02 (Avanzado) | rm_level1_enemy_corridor | El enemigo patrulla colisiona por la espalda al bajar de la escalera | La velocidad del enemigo (3 px/step) no deja tiempo de reacción | Se redujo la velocidad de obj_enemy_patrol a 1.8 px/step en esa zona |
| Tester_03 (Casual) | rm_level2_boss_entry | Ignora la puerta y regresa sobre sus pasos confundido | La puerta de transición se mimetiza con el tilemap de fondo | Se añadió una luz tenue y un sprite de marco contrastante a obj_door |
| Tester_04 (Promedio) | rm_level1_climb | Frustración visible al repetir toda la sala tras fallar el último salto | Falta de un punto intermedio de guardado en una sala de 1800 px de alto | Se colocó un obj_checkpoint a mitad del ascenso vertical |
Ajuste de parámetros en GameMaker: El archivo de balance centralizado
Cuando ajustas la dificultad de tu juego, dispersar variables numéricas en docenas de objetos individuales hace que el balance sea caótico y propenso a errores. Si decides que el personaje debe saltar un 10% más alto, tener que abrir `obj_player`, luego cambiar la altura de los techos en 4 salas distintas y después recalcular los fosos es ineficiente.
Una buena práctica en GameMaker consiste en centralizar las constantes de equilibrio en un Script accesible globalmente o en macros bien organizadas. Creemos un Script en nuestro árbol de recursos denominado `scr_game_balance`:
/// @description scr_game_balance
// Definición de constantes y parámetros maestros de jugabilidad
// Tolerancias de salto del jugador
#macro PLAYER_GRAVITY 0.45
#macro PLAYER_WALK_SPEED 3.8
#macro PLAYER_JUMP_FORCE -8.8
#macro PLAYER_COYOTE_FRAMES 6
#macro PLAYER_BUFFER_FRAMES 5
// Parámetros de enemigos estándar
#macro ENEMY_PATROL_SPEED 1.6
#macro ENEMY_AGGRETION_RANGE 128
// Reglas de diseño y balance espacial
#macro CHECKPOINT_HEAL_PLAYER true
#macro INVULNERABILITY_TIME 60 // Duración de frames de inmunidad tras recibir dañoPosteriormente, en el Create Event de `obj_player`, simplemente inicializamos nuestras variables mecánicas vinculándolas a estas macros:
/// @description Create Event de obj_player con macros de balance
grav = PLAYER_GRAVITY;
walk_speed = PLAYER_WALK_SPEED;
jump_speed = PLAYER_JUMP_FORCE;
// Variables de tolerancia dinámica
coyote_max = PLAYER_COYOTE_FRAMES;
coyote_counter = 0;
buffer_max = PLAYER_BUFFER_FRAMES;
buffer_counter = 0;
hspd = 0;
vspd = 0;
grounded = false;De este modo, cuando una sesión de playtesting revele que los saltos se sienten ligeramente pesados o que el movimiento horizontal requiere mayor dinamismo, bastará con modificar un único archivo para reequilibrar todo el proyecto de manera coherente.
Ejercicios Prácticos
**Ejercicio 1: Implementación de la caja negra de playtesting** Crea el objeto `obj_playtest_manager` con las especificaciones mostradas en la lección. Agrégalo a la primera sala de tu juego y asegúrate de que sea persistente. Configura la lógica necesaria para que, cada vez que el jugador muera, las coordenadas (x, y) de la pérdida se almacenen y se muestren en la consola con `show_debug_message()`. Verifica su funcionamiento dejando que el jugador caiga en un foso intencionadamente y comprobando la salida en la ventana de compilación.
**Criterio de validación:** Al iniciar el juego, presionar F1 debe ocultar y mostrar la interfaz de métricas en la esquina superior izquierda. La consola de GameMaker debe registrar un renglón cada vez que se pierda una vida, detallando la sala y la posición exacta sin producir caídas de frames.
**Ejercicio 2: Auditoría espacial de métricas de salto** Construye una sala de pruebas especial (`rm_testing_grounds`) fuera del flujo regular del juego. En ella, erige cinco plataformas consecutivas separadas por distancias progresivas de 2, 3, 4, 5 y 6 tiles de 32 píxeles de ancho respectivamente. Haz saltar a tu personaje con la velocidad estándar y registra en qué distancia el salto se vuelve matemáticamente imposible sin impulsos externos.
**Criterio de validación:** Determinar con precisión el límite físico de alcance horizontal en tiles de tu avatar. Documenta el resultado y asegúrate de que ninguna fosa obligatoria en tus niveles de historia (`rm_level1`, `rm_level2`) supere el 80% de ese valor máximo.
**Ejercicio 3: Simulación de sesión de prueba externa** Exporta una compilación ejecutable o haz que una persona externa (compañero, familiar o amigo que no haya visto el código) juegue tus dos niveles completos sin recibir ninguna indicación verbal por tu parte. Toma notas en una hoja utilizando la estructura de la Matriz de Observación de Playtesting explicada arriba. Identifica al menos dos puntos de fricción o duda y aplica una modificación de diseño o código para solucionarlos.
**Criterio de validación:** Presentar una tabla completa con al menos 2 problemas detectados, sus causas de raíz analizadas y los cambios efectuados en el nivel o en el script de balance (`scr_game_balance`).
Resumen
- El playtesting evalúa la experiencia subjetiva, la claridad y el ritmo de juego, a diferencia del debugging que se enfoca en resolver errores de software.
- La frustración no proviene de la dificultad, sino de la falta de información, la inconsistencia de reglas y el castigo desproporcionado.
- Las decisiones de diseño deben basarse en métricas espaciales cuantificables, dejando siempre un margen de tolerancia del 15% al 30% respecto al límite físico máximo del personaje.
- Implementar telemetría básica (conteo de muertes por sala, cronometraje y registros en consola) permite diagnosticar cuellos de botella objetivamente sin depender solo de opiniones.
- Durante una prueba con usuarios reales, el desarrollador debe guardar silencio absoluto, observar el lenguaje corporal y formular preguntas abiertas al finalizar.
- Herramientas como el Coyote Time, el Jump Buffering y las guías visuales (breadcrumbing) eliminan la percepción de injusticia técnica.
- Centralizar los valores de movimiento y enemigos mediante macros en scripts específicos acelera el proceso de equilibrado iterativo.
Practica lo aprendido
Ejercicio 1 · 1 punto
Relaciona cada una de las causas de frustración destructiva explicadas en la lección con la situación que la ejemplifica:
- Falta de información (Ceguera)
- Inconsistencia de reglas
- Castigo desproporcionado
Ejercicio 2 · 1 punto
¿Cuál es la diferencia fundamental entre la depuración técnica (debugging) y las pruebas de juego (playtesting)?
Ejercicio 3 · 1 punto
Para que el objeto `obj_playtest_manager` conserve los datos a través de distintas salas, su propiedad debe configurarse en `true`. Además, al diseñar abismos en niveles estándar, la distancia máxima recomendada debe situarse entre el % y el 85% de la capacidad matemática del personaje para absorber imprecisiones humanas.
Ingresa para guardar tu progreso — Ingresar