Introducción al Desarrollo de Videojuegos con GameMaker · Módulo 5: Colisiones, gravedad y plataformas.

Aplicación de gravedad constante

Clase 21 de 60 · 50 min

También disponible en: EN

Introducción a la simulación física en videojuegos 2D

En el desarrollo de videojuegos de plataformas bidimensionales, la gravedad no es simplemente una fuerza de atracción planetaria como la describe la física clásica de Isaac Newton; es, ante todo, una herramienta de diseño lúdico. La forma en que un personaje cae, la aceleración con la que gana velocidad hacia abajo y la rapidez con la que reacciona a los comandos del jugador determinan de manera directa la sensación de juego o 'game feel'. Si la gravedad es demasiado baja, el personaje se sentirá flotante, impreciso y difícil de controlar, como si estuviera caminando en la Luna. Por el contrario, si la aceleración descendente es excesiva, los saltos parecerán secos, pesados e impredecibles.

Para construir esta mecánica con total solidez técnica, es indispensable comprender cómo GameMaker procesa las coordenadas espaciales y el tiempo de ejecución. A diferencia de un simulador científico continuo, un motor de videojuegos actualiza el estado del mundo en intervalos discretos llamados fotogramas o 'steps'. En cada uno de estos ciclos, nuestro código debe calcular de forma determinista cuánto se incrementa la velocidad vertical y cómo impacta ese cambio en la posición vertical del objeto. En esta lección, implementaremos este sistema paso a paso utilizando código nativo en GML Code, prescindiendo deliberadamente de los sistemas automáticos del motor para otorgarte un control absoluto sobre el movimiento y las futuras colisiones.

El sistema de coordenadas de GameMaker y el eje Y invertido

Uno de los primeros obstáculos conceptuales que enfrentan quienes se inician en la programación de videojuegos es la orientación de los ejes cartesianos. En las matemáticas tradicionales que se enseñan en la escuela, el punto de origen (0, 0) suele ubicarse en el centro o en la esquina inferior izquierda de un plano cartesiano, donde los valores de X crecen hacia la derecha y los valores de Y crecen hacia arriba. Sin embargo, en la computación gráfica y en GameMaker en particular, el estándar proviene de la forma en que los antiguos monitores de tubo de rayos catódicos (CRT) dibujaban las líneas en pantalla: de izquierda a derecha y de arriba hacia abajo.

Por lo tanto, en una Room de GameMaker, el origen de coordenadas (x = 0, y = 0) se sitúa exactamente en la esquina superior izquierda de la sala. Esto tiene dos consecuencias inmediatas y vitales para la programación de cualquier movimiento:

1. El eje horizontal (x) funciona de manera intuitiva: aumentar el valor de x desplaza el objeto hacia la derecha, mientras que disminuir el valor de x lo desplaza hacia la izquierda. 2. El eje vertical (y) se encuentra invertido respecto a la intuición escolar: aumentar el valor de y desplaza el objeto hacia abajo (hacia el fondo de la pantalla), mientras que restar o disminuir el valor de y eleva al objeto hacia arriba (hacia el techo de la sala).

Entender esta inversión del eje Y es el requisito indispensable para programar la gravedad. Si la gravedad es una aceleración que empuja los cuerpos hacia el suelo, en GameMaker dicha aceleración debe ser una cantidad positiva que se sume de manera progresiva a la posición vertical del personaje en cada fotograma transcurrido.

Diagrama del sistema de coordenadas 2D en GameMaker mostrando el origen en la esquina superior izquierda, eje X positivo hacia la derecha y eje Y positivo hacia abajo.
Sistema de coordenadas cartesianas en GameMaker: la gravedad suma unidades positivas hacia abajo en el eje Y. · Imagen generada con IA

¿Variables integradas de GameMaker o variables personalizadas?

Quienes exploran la documentación básica de GameMaker descubrirán rápidamente que el motor posee variables internas predeterminadas como 'vspeed', 'hspeed', 'speed', 'direction' y 'gravity'. Si asignas un valor a 'gravity' (por ejemplo, gravity = 0.5), GameMaker aplicará automáticamente una aceleración hacia abajo a la instancia sin requerir una sola línea adicional de cálculo. Sin embargo, en el desarrollo profesional e independiente de juegos de plataformas 2D, el uso de estas variables integradas se desaconseja de forma unánime.

La razón de esta advertencia radica en el orden interno de ejecución de eventos del motor. Cuando utilizas 'vspeed' y 'gravity', GameMaker suma automáticamente la velocidad a la posición 'y' entre el evento Step y el evento End Step, antes de que puedas intervenir para validar si el personaje va a atravesar una plataforma, una trampa o un muro sólido. Esto genera comportamientos erráticos, tales como personajes que se hunden parcialmente en el suelo, vibraciones visuales (jittering) y fallos graves al calcular colisiones milimétricas.

La solución estándar y robusta consiste en gestionar la velocidad y la aceleración mediante variables personalizadas de instancia, tales como 'vspd' (velocidad vertical) y 'grav' (fuerza de aceleración de la gravedad). Al calcular nosotros mismos la acumulación de velocidad y el cambio de posición dentro del evento Step, conservamos el control absoluto de cada píxel recorrido antes de que el motor dibuje el fotograma en pantalla.

💡 Regla de oro: En plataformas 2D de alta precisión, jamás mezcles las variables integradas (vspeed, hspeed, gravity) con tus variables personalizadas de movimiento (vspd, hspd, grav). Mantén todo el control dentro de tus propios scripts y eventos.

Definición de variables en el evento Create de obj_player

El primer paso técnico es inicializar las variables que definirán el comportamiento físico de nuestro personaje principal, al que llamaremos 'obj_player'. Recuerda que el evento Create se ejecuta una única vez en el momento exacto en que la instancia del objeto nace dentro de la Room. Es el lugar idóneo para declarar propiedades, parámetros y valores iniciales de movimiento.

Abramos nuestro objeto 'obj_player' en el espacio de trabajo de GameMaker, seleccionemos el evento Create y añadamos el siguiente bloque de código en GML Code:

// Evento Create de obj_player

// Velocidades de desplazamiento
hspd = 0;              // Velocidad horizontal actual en píxeles por frame
vspd = 0;              // Velocidad vertical actual en píxeles por frame

// Parámetros de aceleración y límites físicos
grav = 0.3;            // Constante de gravedad (aceleración por frame)
vspd_max = 9.0;        // Velocidad terminal o límite máximo de caída

// Parámetros de control horizontal y salto (declarados en el Módulo 4)
walk_spd = 3.5;        // Velocidad máxima de caminata
jump_spd = -7.5;       // Impulso inicial de salto (negativo para subir)

Analicemos la función de cada variable introducida para la gravedad:

- 'vspd': Almacena la cantidad de píxeles que la instancia debe desplazarse verticalmente en el fotograma actual. Un valor de 0 indica reposo; un valor negativo (por ejemplo, -7.5) significa que el personaje se eleva; un valor positivo (por ejemplo, 4.2) significa que desciende. - 'grav': Representa la aceleración de gravedad. En cada fotograma, sumaremos esta cantidad a 'vspd'. Un valor de 0.3 píxeles/frame² ofrece una respuesta elástica y controlable, típica de juegos retro de 16 bits. - 'vspd_max': Representa la velocidad terminal. En el mundo real, la resistencia aerodinámica del aire impide que los cuerpos aceleren de forma infinita en caída libre. En un videojuego, una velocidad terminal es crucial no solo por estética, sino para evitar que un personaje caiga tan rápido que atraviese plataformas delgadas en un solo fotograma (fenómeno conocido como 'tunneling').

Implementación del incremento gradual en el evento Step

El evento Step se ejecuta de forma constante y periódica: exactamente una vez por cada fotograma del juego. Si nuestro proyecto está configurado a 60 fotogramas por segundo (fps), el código escrito dentro del evento Step se evaluará y ejecutará 60 veces en un segundo. Es aquí donde transformamos la aceleración matemática en movimiento visual perceptible.

La fórmula fundamental de la cinemática para este proceso consta de dos operaciones secuenciales:

1. Actualizar la velocidad mediante la aceleración: velocidad_nueva = velocidad_anterior + aceleración 2. Actualizar la posición mediante la velocidad: posición_nueva = posición_anterior + velocidad_nueva

Veamos cómo se traduce esto a código limpio y eficiente dentro del evento Step de 'obj_player':

// Evento Step de obj_player

// 1. Aplicación gradual de la gravedad
vspd += grav;

// 2. Limitación a la velocidad terminal (Terminal Velocity)
if (vspd > vspd_max) {
    vspd = vspd_max;
}

// 3. Aplicación del movimiento vertical a la coordenada espacial
y += vspd;

El operador '+=' es una forma abreviada de escribir 'vspd = vspd + grav;'. En cada ciclo de 1/60 de segundo, 'vspd' incrementa su magnitud en 0.3 unidades. Cuando este valor supera el límite establecido en 'vspd_max' (9.0 píxeles por fotograma), la estructura condicional 'if' interviene y trunca el exceso, manteniendo la velocidad fijada en su valor tope.

Uso de funciones nativas para acotar la velocidad: clamp() y min()

Aunque la estructura condicional 'if' es completamente válida y muy intuitiva para principiantes, GameMaker pone a nuestra disposición funciones matemáticas integradas que permiten escribir un código más conciso, expresivo y elegante. Una de las más utilizadas en este escenario es la función 'min()' o, de manera aún más completa, la función 'clamp()'.

La función 'min(val1, val2, ...)' devuelve el número más bajo entre todos los argumentos provistos. Por lo tanto, podemos resumir el incremento de gravedad y el límite de caída en una sola línea:

// Alternativa elegante usando min()
vspd = min(vspd + grav, vspd_max);

Si 'vspd + grav' resulta en 4.5, y 'vspd_max' es 9.0, la función escoge 4.5. Sin embargo, si 'vspd + grav' alcanza 9.3, la función descarta ese valor y devuelve 9.0, garantizando que el personaje jamás sobrepase la velocidad máxima permitida.

Por otro lado, la función 'clamp(val, min, max)' restringe un valor numérico entre un límite inferior y un límite superior. Esto resulta especialmente útil cuando queremos garantizar que el personaje tampoco ascienda a una velocidad desmedida durante un salto propulsado:

// Alternativa bidireccional usando clamp()
vspd += grav;
vspd = clamp(vspd, -12.0, vspd_max);

Análisis paso a paso del comportamiento de caída libre

Para visualizar exactamente qué ocurre en la memoria del motor y en las coordenadas de la pantalla, analicemos el comportamiento de una instancia de 'obj_player' que parte del reposo (vspd = 0) en la posición vertical y = 100, con una gravedad grav = 0.5 y una velocidad terminal vspd_max = 2.0 a lo largo de los primeros 6 fotogramas:

Framevspd inicialOperación (+ grav)vspd finaly anteriory nueva (y + vspd)
Frame 00.00.0 + 0.50.5100.0100.5
Frame 10.50.5 + 0.51.0100.5101.5
Frame 21.01.0 + 0.51.5101.5103.0
Frame 31.51.5 + 0.52.0103.0105.0
Frame 42.02.0 + 0.5 -> min(2.5, 2.0)2.0105.0107.0
Frame 52.02.0 + 0.5 -> min(2.5, 2.0)2.0107.0109.0

Observa con atención la distancia recorrida en cada fotograma sucesivo: en el Frame 0 desciende apenas 0.5 píxeles; en el Frame 1 desciende 1.0 píxel; en el Frame 2 desciende 1.5 píxeles; y a partir del Frame 3, habiendo alcanzado la velocidad terminal de 2.0 píxeles por frame, el descenso se vuelve rectilíneo y uniforme, avanzando exactamente 2.0 píxeles por cada ciclo de reloj. Esta suave transición entre reposo, aceleración parabólica y velocidad constante es lo que engaña al ojo humano para percibir un objeto con peso y masa creíbles en pantalla.

Integración de gravedad con el salto desarrollado en el Módulo 4

En el módulo anterior aprendiste a leer las teclas del jugador y a aplicar una velocidad vertical negativa cuando se pulsa la barra espaciadora o la tecla de flecha hacia arriba. Al combinar el salto con la gravedad constante, el cálculo matemático genera de manera natural y automática una trayectoria parabólica perfecta, sin necesidad de emplear funciones trigonométricas como senos o cosenos.

Veamos cómo interactúan ambas mecánicas dentro del evento Step completo de nuestro objeto 'obj_player':

// Evento Step de obj_player

// 1. Captura de entradas del jugador
var _key_jump = keyboard_check_pressed(vk_space);

// 2. Ejecución del impulso de salto (si se presiona la tecla)
// Nota: Más adelante en este módulo limitaremos esto a cuando esté en el suelo
if (_key_jump) {
    vspd = jump_spd;
}

// 3. Aplicación de la gravedad constante
vspd += grav;

// 4. Control de velocidad terminal
if (vspd > vspd_max) {
    vspd = vspd_max;
}

// 5. Aplicación del desplazamiento al eje Y
y += vspd;

Cuando el jugador presiona la barra espaciadora, 'vspd' adquiere de golpe un valor negativo alto, como -7.5. En el primer fotograma tras el salto, 'y' disminuirá en 7.5 píxeles, lanzando al personaje hacia el cielo. En los fotogramas subsiguientes, la gravedad (+0.3) irá erosionando progresivamente ese impulso negativo:

- Frame 1: vspd = -7.2 (el personaje sube rápido) - Frame 10: vspd = -4.5 (el personaje sigue subiendo, pero frena su ascenso) - Frame 25: vspd = 0.0 (ápice o punto más alto del salto; velocidad vertical nula durante un instante) - Frame 26: vspd = +0.3 (el personaje comienza a caer hacia abajo) - Frame 40: vspd = +4.5 (el personaje cae aceleradamente)

Esta curva cinemática es idéntica a la que experimenta un proyectil real en física elemental, producida únicamente mediante sumas y comparaciones lógicas ejecutadas 60 veces por segundo.

El problema de la caída infinita y preparación para colisiones

Si ejecutas el juego en este momento tras haber ingresado el código anterior, notarás un problema evidente: el personaje cae suavemente, acelera, alcanza su velocidad máxima de caída y continúa descendiendo sin detenerse, atravesando el límite inferior de la ventana y perdiéndose en el vacío infinito. Esto es completamente normal y esperado en esta etapa.

Las computadoras no comprenden el concepto intuitivo de 'suelo' a menos que nosotros programemos las comprobaciones matemáticas que impidan que un sprite ocupe el mismo espacio que otro. GameMaker dibuja los fondos y los objetos exactamente donde le indicamos; si incrementamos la coordenada 'y' en cada fotograma sin validar obstáculos, la instancia simplemente continuará sumando coordenadas en dirección hacia el infinito positivo.

En las siguientes clases de este módulo implementaremos las funciones 'place_meeting()' y la gestión de objetos sólidos ('obj_solid' o 'obj_wall') para detener el avance de 'vspd' en el milímetro exacto en que la base del personaje entre en contacto con una plataforma. No obstante, haber aislado y perfeccionado primero la gravedad asegura que el motor de físicas sea modular, limpio y sumamente fácil de depurar cuando integremos las colisiones horizontales y verticales.

Buenas prácticas y errores frecuentes al aplicar gravedad

Al trabajar con física básica en GameMaker, los principiantes suelen incurrir en errores recurrentes que comprometen la estabilidad de sus proyectos. Presta especial atención a las siguientes recomendaciones:

1. **Olvidar la velocidad terminal:** Dejar que la gravedad sume indefinidamente a 'vspd' puede provocar que la velocidad vertical alcance valores de 50 o 100 píxeles por fotograma en caídas prolongadas. Esto causará que el personaje 'salte' a través de plataformas delgadas sin llegar a detectar la colisión entre un frame y el siguiente. 2. **Confundir los signos de dirección:** Recuerda que para caer, la gravedad debe sumarse (vspd += grav), y para saltar o subir, la velocidad debe ser negativa (jump_spd = -8). Invertir estos signos hará que el personaje flote hacia el techo de la sala en una aceleración descontrolada. 3. **Declarar variables en el evento erróneo:** Si declaras 'vspd = 0;' dentro del evento Step en lugar del evento Create, la velocidad vertical se reiniciará a cero en cada fotograma. El personaje nunca llegará a acelerar porque la asignación sobrescribirá continuamente el cálculo anterior. 4. **Uso de números con precisión flotante:** GameMaker maneja números de punto flotante de 64 bits de forma predeterminada. Es perfectamente lícito y recomendable usar valores decimales pequeños como 0.25, 0.3 o 0.45 para la gravedad. No restrinjas tus variables a números enteros si buscas un control suave y preciso.

💡 Consejo de depuración: Puedes supervisar en tiempo real el valor de 'vspd' y 'y' dibujándolos en pantalla mediante la función 'draw_text(x, y - 20, string(vspd));' dentro del evento Draw, o abriendo la pestaña 'Debug' en GameMaker durante la ejecución del proyecto.

Ejercicios prácticos

Pon a prueba tus conocimientos y consolida la lógica de aceleración completando los siguientes ejercicios:

**Ejercicio 1: Cálculo manual de trayectoria y aceleración** Un personaje en reposo parte de la coordenada vertical y = 250. Posee una gravedad grav = 0.4 y una velocidad máxima de caída vspd_max = 1.0. Sin encender el ordenador, calcula a mano en tu libreta el valor exacto de 'vspd' y la coordenada 'y' en los fotogramas 0, 1, 2, 3 y 4. Comprueba que comprendes el momento exacto en que la velocidad deja de aumentar y se estabiliza en la velocidad terminal.

**Ejercicio 2: Ajuste de gravedad dual (Salto con caída rápida)** En muchos juegos clásicos (como Mario Bros o Hollow Knight), el personaje cae más rápido de lo que sube, otorgando una sensación de peso muy satisfactoria. Modifica el evento Step de 'obj_player' para implementar una condición: si 'vspd < 0' (el personaje está ascendiendo), la gravedad aplicada debe ser menor (por ejemplo, grav_subida = 0.25). Si 'vspd >= 0' (el personaje ha llegado al tope o está cayendo), la gravedad aplicada debe ser mayor (por ejemplo, grav_bajada = 0.55). Comprueba en GameMaker cómo cambia la respuesta visual del salto.

**Ejercicio 3: Detección y corrección de bugs de inicialización** Un estudiante intenta implementar la gravedad en su juego, pero su personaje cae a velocidad constante sin acelerar en ningún momento, desplazándose exactamente a 3 píxeles por fotograma hacia abajo. Al inspeccionar su código, se encuentra lo siguiente en el evento Step: ```gml vspd = 0; vspd += 3; y += vspd; ``` Identifica el error conceptual cometido por el estudiante, explica por qué la aceleración no ocurre y reescribe el código correctamente dividiéndolo entre el evento Create y el evento Step como corresponde.

Resumen

  • En GameMaker, el eje Y se encuentra invertido: el origen (0,0) está en la esquina superior izquierda, por lo que los valores positivos de Y se desplazan hacia abajo.
  • Para lograr control milimétrico sobre las colisiones y el movimiento, se utilizan variables personalizadas (hspd, vspd, grav, vspd_max) en lugar de las variables automáticas de GameMaker (speed, vspeed, gravity).
  • Las variables de velocidad y aceleración deben inicializarse en el evento Create para evitar que se reinicien en cada ciclo del juego.
  • La gravedad se simula sumando de forma constante la aceleración a la velocidad vertical (vspd += grav) en cada fotograma dentro del evento Step.
  • La velocidad terminal (vspd_max) es obligatoria para impedir que el personaje adquiera aceleraciones excesivas que puedan provocar que atraviese plataformas o suelos delgados.
  • La trayectoria parabólica natural de un salto se produce espontáneamente al combinar un impulso vertical negativo inicial con la aceleración positiva continua de la gravedad.

Practica lo aprendido

Ejercicio 1 · 1 punto

¿Por qué en GameMaker sumar un valor positivo a la posición vertical desplaza al personaje hacia abajo (hacia el suelo)?

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

Ejercicio 2 · 1 punto

Ordena cronológicamente los pasos fundamentales que se ejecutan en el evento Step de obj_player para aplicar la gravedad y mover al personaje de manera controlada:

  1. 1Modificar la coordenada espacial vertical sumándole la velocidad final (y += vspd)
  2. 2Comprobar si la velocidad vertical sobrepasa el límite permitido y truncarla al valor de vspd_max
  3. 3Sumar la aceleración de gravedad a la velocidad vertical (vspd += grav)
Practicas sin cuenta: no se guarda nada. Regístrate para guardar tu progreso.

Ejercicio 3 · 1 punto

Completa el código del evento Step con los nombres de las variables correspondientes vistas en la lección: // 1. Aplicación gradual de la gravedad vspd += ; // 2. Limitación a la velocidad terminal if (vspd > ) { vspd = ; } // 3. Aplicación del movimiento a la posición vertical += vspd;

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

Ingresa para guardar tu progreso — Ingresar