Suavidad a cualquier tasa de fotogramas
Parte de la sección Sonido y sensaciones del juego del Journey de Pygame de Coddy. Lección 19 de 23.
x += (target_x - x) * 0.1 avanza una décima parte del camino por fotograma. A 60 fotogramas por segundo, son 60 pasos por segundo; en una computadora lenta que solo alcanza 30, son 30 pasos, y el círculo tarda el doble en llegar. Pulsa F para verlo: el juego funciona a 30 y todo se desplaza más lentamente.
La solución es medir el tiempo. clock.tick() devuelve los milisegundos transcurridos desde el último fotograma. Al dividirlos entre 1000, se obtiene dt, la duración del fotograma en segundos: aproximadamente 0.017 a 60 fotogramas por segundo y 0.033 a 30.
dt = 1 / 60 # antes del bucle
...
step = min(1, 6 * dt)
x += (target_x - x) * step
...
dt = clock.tick(fps) / 1000Un fotograma más largo implica un paso mayor, por lo que el círculo recorre la misma distancia en el mismo tiempo con cualquier frecuencia de fotogramas. El 6 determina la velocidad: a 60 fotogramas por segundo, 6 * dt es aproximadamente 0.1, la misma décima parte que antes. min(1, ...) evita que un fotograma muy largo sobrepase el objetivo.
Lo mismo se aplica al movimiento simple: player_x += 240 * dt equivale a 240 píxeles por segundo, independientemente de la frecuencia de fotogramas. La mayoría de los juegos pequeños fijan la frecuencia con tick(60) y nunca necesitan esto; usa dt cuando la frecuencia pueda cambiar.
Desafío
IntermedioF cambia el juego entre 60 y 30 fotogramas por segundo. Haz que la interpolación tarde lo mismo en ambos casos:
- Asigna
dt = 1 / 60antes del bucle. - Al final del bucle, asigna
dt = clock.tick(fps) / 1000. - Muévete con
step = min(1, 6 * dt)en lugar de 0.1:x += (target_x - x) * step, y haz lo mismo cony.
Pruébalo tú mismo
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 360))
pygame.display.set_caption("Any Frame Rate")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 28)
x, y = 240, 180
target_x, target_y = 240, 180
fps = 60
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
target_x, target_y = event.pos
elif event.type == pygame.KEYDOWN and event.key == pygame.K_f:
fps = 30 if fps == 60 else 60
x += (target_x - x) * 0.1
y += (target_y - y) * 0.1
screen.fill((30, 30, 46))
pygame.draw.circle(screen, (80, 200, 220), (x, y), 12)
label = font.render(f"{fps} FPS (F to change)", True, (240, 240, 240))
screen.blit(label, (10, 10))
pygame.display.flip()
clock.tick(fps)
pygame.quit()
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Sonido y sensaciones del juego
1Efectos de sonido
Cargar un sonidoSonido al ocurrir un eventoVolumen del sonidoSonidos y teclas presionadas4Sacudida de pantalla y destello de impacto
Sacudida de pantallaDestello de impactoPausa tras el impactoMantén la sutileza5Movimiento suave
Moverse hacia un objetivoUna cámara suaveEfecto pop al recogerSuavidad a cualquier tasa de fotogramasPractica por tu cuenta: Playground de Pygame