Parte: 0 — Fundamentos y prerrequisitos · Fuente: Ian Millington, Game Physics Engine Development ⏱️ Duración estimada: 100 min · Nivel: Fundamentos
Casi todo lo que se mueve en un juego —un salto, una bala, una caja que cae, un coche que frena— se describe con tres cantidades: posición, velocidad y aceleración. Entender cómo se relacionan y cómo avanzarlas frame a frame (integración) es la base de cualquier motor de física.
En esta clase construirás un mini-simulador en Python: aplicarás gravedad, verás por qué la integración de Euler semi-implícita es estable mientras la explícita "gana energía", y añadirás drag para frenar el movimiento de forma realista. Todo imprimiendo la trayectoria numérica frame a frame.
Al finalizar, el alumno podrá:
F = m·a para obtener aceleración a partir de fuerzas y masa.| # | Tema | Por qué importa |
|---|---|---|
| 1 | Posición, velocidad, aceleración | Son el estado mínimo de todo cuerpo en movimiento. |
| 2 | Gravedad | Fuerza constante que da peso a los objetos. |
| 3 | F = m·a | Convierte fuerzas acumuladas en aceleración. |
| 4 | Integración de Euler explícita | La forma más simple... y también inestable. |
| 5 | Euler semi-implícita | El estándar práctico en juegos por su estabilidad. |
| 6 | Drag / fricción | Frena el movimiento y evita velocidades infinitas. |
| 7 | Salto como impulso | Un cambio instantáneo de velocidad hacia arriba. |
dt.a = F / m, así sumas fuerzas y obtienes aceleración.Necesitas Python 3.10+ con la librería estándar (nada externo). Descárgalo de https://www.python.org/downloads/ y verifica con python --version. Un editor como Visual Studio Code https://code.visualstudio.com/ facilita ejecutar los scripts. El libro de referencia es Game Physics Engine Development de Ian Millington; su web de recursos es https://www.gamephysicsengine.com/. La documentación de Python está en https://docs.python.org/3/.
Crea fisica_lab.py:
GRAVEDAD = -9.8 # m/s^2, hacia abajo
DT = 0.1 # 10 frames por segundo (para ver números claros)
# Estado inicial: en el suelo, saltando hacia arriba
pos_y = 0.0
vel_y = 15.0 # impulso del salto (m/s hacia arriba)
En el semi-implícito primero actualizamos la velocidad con la aceleración y luego movemos la posición con esa velocidad nueva:
print("=== Euler SEMI-IMPLICITO ===")
pos_y, vel_y = 0.0, 15.0
frame = 0
while pos_y >= 0.0:
vel_y += GRAVEDAD * DT # 1) velocidad primero
pos_y += vel_y * DT # 2) posicion con la velocidad nueva
frame += 1
print(f"Frame {frame:2d}: altura = {pos_y:6.2f} m, vel = {vel_y:6.2f} m/s")
print(f"Aterrizo en el frame {frame}\n")
Verás la altura subir, frenarse en el pico y volver a bajar hasta cruzar el suelo.
En el explícito movemos con la velocidad vieja y luego actualizamos la velocidad:
print("=== Euler EXPLICITO ===")
pos_y, vel_y = 0.0, 15.0
frame = 0
while pos_y >= 0.0 and frame < 40:
pos_y += vel_y * DT # 1) posicion con la velocidad vieja
vel_y += GRAVEDAD * DT # 2) velocidad despues
frame += 1
print(f"Frame {frame:2d}: altura = {pos_y:6.2f} m, vel = {vel_y:6.2f} m/s")
Compara ambas trayectorias: el explícito llega un poco más alto y tarda más en caer porque "gana" energía en cada paso. Con dt grandes o resortes, esa energía extra hace que la simulación explote. Por eso los juegos prefieren el semi-implícito.
En vez de fijar la aceleración, acumulamos fuerzas y dividimos por la masa:
masa = 2.0
def acumular_fuerzas():
peso = masa * GRAVEDAD # F = m*g
return peso
pos_y, vel_y = 0.0, 15.0
for frame in range(1, 6):
fuerza = acumular_fuerzas()
accel = fuerza / masa # a = F / m -> vuelve a dar GRAVEDAD
vel_y += accel * DT
pos_y += vel_y * DT
print(f"Frame {frame}: altura {pos_y:.2f}, vel {vel_y:.2f}")
El drag resta energía multiplicando la velocidad por un factor menor que 1 cada frame:
DRAG = 0.9 # 10% de la velocidad se pierde por frame
pos_y, vel_y = 0.0, 15.0
frame = 0
while pos_y >= 0.0:
vel_y += GRAVEDAD * DT
vel_y *= DRAG # amortiguacion
pos_y += vel_y * DT
frame += 1
print(f"Frame {frame:2d}: altura {pos_y:6.2f}, vel {vel_y:6.2f}")
print("Con drag el salto es mas bajo y cae mas suave.")
DT a 0.016 (60 FPS) y comprueba que la trayectoria es más suave.pos_x y vel_x con velocidad horizontal constante (un salto en parábola).DRAG = 0.98 y DRAG = 0.7; describe cómo cambia la altura del salto.DT grande (0.5), compara qué integrador explota antes.paso_semi_implicito(pos, vel, accel, dt) reutilizable que devuelva (pos, vel) nuevos.Simula un salto en 2D con Euler semi-implícito: impulso vertical de 12 m/s, velocidad horizontal de 4 m/s, gravedad -9.8 y DT = 0.1. Imprime (x, y) por frame hasta que aterrice y reporta el alcance horizontal total y la altura máxima.
Criterio de aceptación: la salida muestra una parábola (Y sube y baja mientras X crece de forma lineal), la altura máxima ronda los 7 m y el objeto aterriza con y < 0; usar el semi-implícito hace que la subida y bajada sean casi simétricas.
| Síntoma / mensaje | Causa y cómo arreglar |
|---|---|
| La simulación "explota" a velocidades enormes | Usaste Euler explícito con dt grande. Cambia a semi-implícito o reduce dt. |
| El salto es cada vez más alto en cada bucle | Sumas la gravedad con signo positivo. La gravedad apunta hacia abajo: negativa. |
| El objeto nunca cae | Olvidaste aplicar la aceleración a la velocidad, o el dt es 0. |
| La física va distinta según los FPS | No multiplicaste por dt. Todo cambio debe escalarse por el paso de tiempo. |
| El drag detiene el objeto de golpe | Factor de drag demasiado bajo. Usa valores cercanos a 1 (0.9–0.99). |
❓ ¿Por qué el semi-implícito es más estable que el explícito? Porque actualiza la velocidad antes de mover; eso hace que la energía del sistema no crezca sin control, evitando que resortes y colisiones exploten.
❓ ¿Qué es dt y por qué debo multiplicar por él? Es el tiempo entre frames. Multiplicar por dt hace que la simulación avance lo mismo independientemente de si el juego corre a 30 o 144 FPS.
❓ ¿La gravedad siempre vale 9.8? No; 9.8 m/s² es la de la Tierra, pero en un juego eliges el valor que se sienta bien. Muchos juegos usan valores mayores para saltos más "arcade".
❓ ¿Un salto es una fuerza o un impulso? Un impulso: un cambio instantáneo de velocidad hacia arriba en el instante del salto, no una fuerza sostenida mientras estás en el aire.
Clase 006 - Matemáticas para juegos III: trigonometría, ángulos y rotaciones
Clase 008 - Programación fundamentos con C#: tipos, control de flujo y funciones