Introducción al Desarrollo de Videojuegos con GameMaker · Módulo 5: Colisiones, gravedad y plataformas.
Colisiones verticales y suelos
Clase 23 de 60 · 60 min
También disponible en: EN
Fundamentos de la colisión vertical en juegos de plataformas
En el desarrollo de videojuegos de plataformas bidimensionales, la física vertical es el pilar sobre el cual descansa toda la experiencia interactiva del usuario. Mientras que el movimiento horizontal define el avance y la exploración a través del escenario, el movimiento vertical gobierna la gravedad, el salto, las caídas y la sensación de peso del personaje. Un sistema de colisiones verticales defectuoso rompe instantáneamente la inmersión del jugador: caídas que atraviesan el suelo (fenómeno conocido técnicamente como 'tunneling'), personajes que se hunden visiblemente varios píxeles dentro del piso antes de detenerse, o saltos que permiten atravesar techos sólidos de manera indeseada.
En GameMaker, el espacio de coordenadas bidimensional posee una convención que todo desarrollador debe dominar desde el primer día: el eje vertical (Y) crece en sentido descendente. El origen de coordenadas (0, 0) se localiza en la esquina superior izquierda de la sala (Room). En consecuencia, cualquier valor positivo aplicado a la velocidad vertical desplaza la instancia hacia abajo (hacia el suelo), mientras que cualquier valor negativo la desplaza hacia arriba (hacia el techo o las alturas del cielo). Esta orientación invertida con respecto al plano cartesiano geométrico tradicional influye directamente en las condiciones lógicas y los cálculos matemáticos de las colisiones.

El problema del movimiento discreto y la solución predictiva
Los motores de videojuegos ejecutan su ciclo principal cuadro a cuadro (frame a frame). Si un personaje posee una velocidad vertical acumulada de 12 píxeles por frame debido a la gravedad y el programador simplemente escribe 'y += vspd;', el motor no desplaza al personaje de manera continua a lo largo de esos 12 píxeles en el tiempo real del mundo, sino que lo 'teletransporta' 12 píxeles más abajo en el siguiente fotograma. Si el suelo sólido se encontraba a tan solo 4 píxeles por debajo de los pies del personaje, la instancia terminará incrustada 8 píxeles dentro del bloque.
Para evitar este error, se utiliza una técnica estándar en la programación de videojuegos 2D conocida como 'resolución predictiva con aproximación por contacto'. En lugar de mover primero la coordenada del objeto y verificar después si colisionó, el algoritmo evalúa de antemano la posición futura del objeto ('y + vspd'). Si se detecta un obstáculo sólido en dicha posición futura, se interrumpe el movimiento brusco y se ejecuta un bucle de aproximación fina que desplaza al personaje píxel a píxel hasta que queda exactamente al ras de la superficie, anulando a continuación la velocidad residual.
Análisis matemático del algoritmo de aproximación vertical
La lógica de colisión vertical se apoya en dos funciones nativas esenciales de GameMaker: 'place_meeting()' y 'sign()'. La función 'place_meeting(x, y, objeto)' toma la máscara de colisión de la instancia actual, la sitúa hipotéticamente en las coordenadas (x, y) suministradas, y comprueba si en esa posición colisionaría con alguna instancia del objeto especificado, retornando un valor booleano ('true' o 'false') sin modificar la posición real de la instancia.
Por su parte, la función matemática 'sign(val)' evalúa el signo del valor numérico que recibe como argumento: si el valor es positivo devuelve 1; si es negativo devuelve -1; y si es exactamente cero devuelve 0. En nuestro contexto, 'sign(vspd)' es una herramienta formidable, ya que condensa la dirección del movimiento vertical en un vector unitario de exactamente un píxel de magnitud: 1 hacia abajo cuando el personaje cae, y -1 hacia arriba cuando el personaje se eleva tras un salto.
// Estructura algorítmica de la colisión vertical predictiva
if (place_meeting(x, y + vspd, obj_bloque))
{
// Mientras no exista colisión a un solo píxel en la dirección de la velocidad
while (!place_meeting(x, y + sign(vspd), obj_bloque))
{
y = y + sign(vspd); // Avanzar un píxel exacto hacia la superficie
}
vspd = 0; // Detener por completo la velocidad vertical acumulada
}
// Si no hay colisión, o una vez resuelta la posición exacta, aplicar movimiento
y = y + vspd;El funcionamiento del bucle 'while' merece una atención especial. Un bucle 'while' repite sus instrucciones internas tantas veces como sea necesario dentro del mismo fotograma antes de que GameMaker dibuje nada en pantalla. Si el personaje cae a 8 píxeles por frame y el suelo está a 3 píxeles, la condición inicial 'place_meeting(x, y + 8, obj_bloque)' se evalúa como verdadera. El bucle 'while' comprobará 'y + 1' (libre), avanzará 1 píxel; comprobará 'y + 2' (libre), avanzará otro píxel; comprobará 'y + 3' (contacto con el suelo). Al detectar el contacto a un píxel de distancia, el bucle se detiene de inmediato. La posición del personaje queda sellada al borde exacto del suelo y la velocidad vertical 'vspd' se establece en 0, evitando que en el siguiente fotograma la gravedad continúe sumando velocidad sobre una superficie sólida.
Pisos y techos: Unificación del tratamiento con sign()
Una de las mayores virtudes de este algoritmo es su simetría matemática. Muchas personas que comienzan a programar cometen el error de crear un bloque de código separado para detectar suelos ('if vspd > 0') y otro totalmente diferente para detectar techos ('if vspd < 0'). Esta duplicación es propensa a errores y complica el mantenimiento del proyecto. Gracias al uso de 'sign(vspd)', el mismo fragmento de código resuelve con idéntica precisión milimétrica ambos escenarios:
1. Cuando el personaje cae ('vspd > 0'): 'sign(vspd)' equivale a +1. El bucle empuja al personaje hacia abajo hasta tocar la parte superior del bloque del piso, fijando 'vspd = 0'. El jugador aterriza limpiamente y la gravedad deja de arrastrarlo hacia el abismo.
2. Cuando el personaje salta ('vspd < 0'): 'sign(vspd)' equivale a -1. El bucle empuja al personaje hacia arriba hasta que su cabeza hace contacto con la base inferior del bloque del techo. En ese instante, al hacer 'vspd = 0', la inercia ascendente del salto se cancela de golpe. En el siguiente fotograma, la gravedad comenzará a sumar su valor positivo, haciendo que el personaje comience a caer de forma natural inmediatamente después de golpearse la cabeza, tal como ocurre en los grandes clásicos de plataformas retro.
Integración completa en el objeto del jugador
Para asegurar la coherencia técnica de nuestro proyecto de plataformas, integraremos las colisiones verticales junto con las horizontales vistas en la clase anterior y el sistema de gravedad básica. Todas las variables deben declararse inicialmente en el evento Create del objeto del jugador ('obj_jugador'), garantizando que existan en la memoria antes de ser leídas o manipuladas.
// ========================================================
// OBJETO: obj_jugador
// EVENTO: Create
// ========================================================
// Variables de movimiento horizontal
hspd = 0; // Velocidad horizontal actual en píxeles por frame
move_speed = 3.5; // Rapidez máxima de desplazamiento lateral
// Variables de movimiento vertical y física
vspd = 0; // Velocidad vertical actual en píxeles por frame
grv = 0.25; // Fuerza de gravedad aplicada por frame
jump_speed = -6.5; // Impulso inicial del salto (negativo hacia arriba)
vspd_max = 9.0; // Velocidad terminal de caída libreCon las variables debidamente inicializadas, el evento Step concentrará toda la lógica de captura de controles, suma de aceleraciones físicas y resolución secuencial de colisiones. El orden de las operaciones en el evento Step es fundamental: primero leemos la entrada del jugador, luego calculamos las velocidades deseadas (incluida la aceleración de gravedad), después resolvemos las colisiones horizontales, a continuación resolvemos las colisiones verticales, y finalmente aplicamos los movimientos.
// ========================================================
// OBJETO: obj_jugador
// EVENTO: Step
// ========================================================
// 1. CAPTURA DE ENTRADAS DEL TECLADO
var _key_left = keyboard_check(vk_left) || keyboard_check(ord("A"));
var _key_right = keyboard_check(vk_right) || keyboard_check(ord("D"));
var _key_jump = keyboard_check_pressed(vk_space) || keyboard_check_pressed(ord("W"));
// 2. CÁLCULO DE MOVIMIENTO HORIZONTAL
var _move = _key_right - _key_left;
hspd = _move * move_speed;
// 3. APLICACIÓN DE GRAVEDAD
// La gravedad incrementa la velocidad descendente hasta alcanzar la velocidad terminal
vspd = vspd + grv;
if (vspd > vspd_max)
{
vspd = vspd_max;
}
// 4. RESOLUCIÓN DE COLISIÓN HORIZONTAL
if (place_meeting(x + hspd, y, obj_bloque))
{
while (!place_meeting(x + sign(hspd), y, obj_bloque))
{
x = x + sign(hspd);
}
hspd = 0;
}
x = x + hspd;
// 5. RESOLUCIÓN DE COLISIÓN VERTICAL (PISOS Y TECHOS)
if (place_meeting(x, y + vspd, obj_bloque))
{
while (!place_meeting(x, y + sign(vspd), obj_bloque))
{
y = y + sign(vspd);
}
vspd = 0;
}
y = y + vspd;Observa con detenimiento que el movimiento se divide estrictamente en dos fases separadas: primero se resuelve y aplica el eje X ('x = x + hspd'), y posteriormente se resuelve y aplica el eje Y ('y = y + vspd'). Esta separación de ejes (Orthogonal Axis Separation) es indispensable. Si intentáramos evaluar simultáneamente la diagonal mediante 'place_meeting(x + hspd, y + vspd, obj_bloque)', el personaje se trabaría al rozar esquinas o caminar adyacente a una pared vertical mientras se encuentra apoyado en el piso.
Diferencias funcionales entre métodos de colisión en GameMaker
En GameMaker existen múltiples funciones destinadas a detectar colisiones, y confundirlas suele generar fallos severos de comportamiento o caídas drásticas de rendimiento. A continuación, se presenta una tabla comparativa de las funciones más relevantes para comprender cuándo y por qué 'place_meeting()' es la opción idónea en este algoritmo de plataformas.
| Función de GML | ¿Qué geometría utiliza? | ¿Qué valor devuelve? | Uso principal recomendado |
|---|---|---|---|
| place_meeting(x, y, obj) | La máscara de colisión (Hitbox) completa de la propia instancia trasladada a (x, y) | Booleano (true / false) | Colisiones sólidas del jugador con el escenario (paredes, pisos y techos). |
| instance_place(x, y, obj) | La máscara completa de la propia instancia trasladada a (x, y) | ID única de la instancia colisionada (o noone) | Interacciones donde necesitamos modificar el objeto tocado (dañar enemigo, romper bloque). |
| position_meeting(x, y, obj) | Un único punto matemático infinitesimal (x, y) | Booleano (true / false) | Detección bajo el cursor del ratón o comprobación de puntos precisos aislados. |
| collision_rectangle(x1, y1, x2, y2, obj, prec, notme) | Un rectángulo definido arbitrariamente mediante cuatro coordenadas | ID de la instancia colisionada (o noone) | Disparadores de zonas (Triggers), campos de visión de enemigos o áreas de daño masivo. |
💡 Regla de oro de las máscaras de colisión: Asegúrate de que el sprite asignado a tu personaje tenga configurada una máscara de colisión de forma rectangular (Rectangle) con dimensiones fijas, o define una máscara específica en la propiedad 'Mask' del objeto. Si la máscara de colisión cambia de tamaño durante una animación (por ejemplo, si la cabeza del sprite sube dos píxeles en un frame), el personaje puede quedar atrapado dentro del suelo o del techo debido al cambio repentino de geometría.
Casos prácticos de estudio y resolución de problemas
Configuración en el entorno de GameMaker paso a paso
Para verificar este comportamiento de forma práctica en tu proyecto, sigue minuciosamente las siguientes instrucciones en el editor de GameMaker:
Paso 1: Creación del bloque sólido. En el panel 'Asset Browser' (Explorador de recursos), haz clic derecho sobre la carpeta 'Sprites' y selecciona 'Create > Sprite'. Asígnale el nombre 'spr_bloque'. En el editor de imágenes, dibuja un cuadrado sólido de 32x32 píxeles utilizando un color oscuro y uniforme para que contraste con el fondo. Conserva el origen del sprite en la esquina superior izquierda (Top Left: x=0, y=0).
Paso 2: Creación del objeto bloque. En el Explorador de recursos, haz clic derecho en 'Objects' y selecciona 'Create > Object'. Nómbralo 'obj_bloque' y asígnale el sprite 'spr_bloque'. No es necesario marcar la casilla 'Solid' nativa de GameMaker; nuestro sistema matemático maneja las colisiones por código sin depender de la física heredada tradicional de versiones antiguas.
Paso 3: Verificación del objeto jugador. Asegúrate de que tu 'obj_jugador' tenga asignado un sprite representativo (por ejemplo, 'spr_jugador' de 32x32 píxeles o 32x48 píxeles). Abre el sprite en el editor, despliega la pestaña 'Collision Mask' a la derecha, define el 'Type' como 'Rectangle' y en 'Origin' asegúrate de ubicarlo en una posición consistente, como 'Bottom Center' (Centro inferior) o 'Top Left' (Esquina superior izquierda). Para este curso, recomendamos 'Bottom Center' para los personajes bípedos, asegurándote de que coincida con la base de los pies.
Paso 4: Diseño de la sala de pruebas (Room). Abre tu sala principal ('Room1'). En la lista de capas (Layers), selecciona la capa de instancias ('Instances'). Arrastra múltiples instancias de 'obj_bloque' para construir un piso horizontal continuo a lo largo del fondo del escenario. Luego, coloca varios bloques formando plataformas elevadas a distintas alturas y, crucialmente, coloca una línea de bloques suspendida en el aire para que actúe como un techo de prueba. Finalmente, sitúa una instancia de 'obj_jugador' en el aire por encima del suelo.
Paso 5: Ejecución y comprobación. Presiona la tecla F5 o haz clic en el botón 'Run' en la barra superior. Al iniciar el juego, el personaje deberá caer por acción de la gravedad y detenerse perfectamente sobre la superficie del suelo sin temblores ni separaciones visibles. Al desplazarte con las flechas o teclas A/D, el jugador deberá deslizarse sobre la superficie continua sin hundirse. Al situarse debajo de la plataforma suspendida y pulsar la tecla de salto (Barra espaciadora o W), el jugador debe golpear el techo y descender de inmediato.
Ejercicios prácticos
Lleva a cabo las siguientes tres actividades para afianzar y extender las habilidades aprendidas en esta lección:
Ejercicio 1 (Verificación y ajuste de velocidad terminal): En el evento Create de 'obj_jugador', modifica la variable 'vspd_max' asignándole un valor bajo de 2.0 y luego un valor extremo de 20.0. Ejecuta la sala en ambos casos y haz que el jugador salte desde la plataforma más alta del nivel. Comprueba visualmente que, a pesar de la altísima velocidad en el segundo caso, el personaje se detiene con idéntica precisión milimétrica sobre la superficie superior de 'obj_bloque' sin traspasarlo. Criterio de comprobación: El personaje reposa exactamente encima del bloque sin quedar hundido ni un solo píxel en ninguno de los dos escenarios.
Ejercicio 2 (Detección de golpe en techo con retroalimentación visual): Implementa una respuesta visual inmediata cuando el jugador impacte contra un techo. Dentro del evento Step de 'obj_jugador', localiza el bloque de colisión vertical. Antes de la instrucción 'vspd = 0;', agrega una condición que verifique si el impacto ocurrió mientras el personaje se movía hacia arriba ('if (vspd < 0)'). Si se cumple dicha condición, cambia momentáneamente el color de fusión del sprite del jugador ejecutando 'image_blend = c_red;'. En la sección donde el jugador no esté colisionando o al tocar el piso, restablece 'image_blend = c_white;'. Criterio de comprobación: El personaje se tiñe de color rojo exclusivamente durante el frame en que su cabeza colisiona contra un techo o bloque superior, retornando a su color original al caer.
Ejercicio 3 (Mecánica de caída rápida - Fast Fall): Agrega una nueva mecánica de juego utilizando la lectura del teclado. En el evento Step, detecta la pulsación de la tecla hacia abajo ('keyboard_check(vk_down) || keyboard_check(ord("S"))'). Si la tecla está presionada y el jugador se encuentra en el aire con velocidad vertical descendente ('vspd > 0'), incrementa artificialmente la gravedad aplicada en ese fotograma al doble ('vspd += grv * 2;'). Criterio de comprobación: Al mantener presionada la tecla hacia abajo durante una caída, el personaje acelera rápidamente hacia el suelo, pero continúa deteniéndose de manera perfecta y segura al hacer contacto con el piso sin colarse a través de él.
Resumen
- El eje vertical (Y) en GameMaker crece hacia abajo: velocidades positivas indican caída (hacia el suelo) y velocidades negativas indican ascenso (hacia el techo).
- El movimiento discreto fotograma a fotograma puede generar atravesamiento de sólidos (tunneling) si la velocidad supera el grosor del obstáculo.
- La resolución predictiva verifica primero la posición futura (y + vspd) con place_meeting() antes de alterar la posición real de la instancia.
- El bucle while (!place_meeting(x, y + sign(vspd), obj_bloque)) aproxima a la instancia píxel a píxel hasta hacer contacto exacto con la superficie.
- La función sign(vspd) unifica matemáticamente el algoritmo: produce +1 durante la caída para pisos y -1 durante el ascenso para techos.
- Al confirmarse la colisión vertical, la variable vspd debe reiniciarse estrictamente a 0 para anular la inercia y permitir la acumulación limpia de gravedad.
- Separar el análisis y aplicación del eje horizontal (X) del eje vertical (Y) previene atascamientos en esquinas y paredes adyacentes.
Practica lo aprendido
Ejercicio 1 · 1 punto
Ordena secuencialmente los pasos del ciclo de ejecución en el evento Step para un correcto manejo del movimiento y las colisiones, según lo indicado en la lección:
- 1Captura de las entradas del teclado del jugador
- 2Resolución de colisiones verticales
- 3Cálculo de velocidades deseadas y aplicación de la aceleración de gravedad
- 4Resolución de colisiones horizontales
Ejercicio 2 · 1 punto
¿Qué valor retorna la función sign(vspd) cuando el personaje se eleva por los aires tras un salto y a qué se debe técnicamente en GameMaker?
Ejercicio 3 · 2 puntos
Completa el bloque de colisión vertical predictiva. Implementa el bucle while para aproximar al personaje píxel a píxel hacia la superficie usando sign(vspd) mientras no colisione, y luego anula la velocidad vertical asignando 0 a vspd.
Tu código no se ejecuta: se comprueban requisitos concretos sobre el texto.
Ingresa para guardar tu progreso — Ingresar