dt (delta time) é o tempo decorrido desde o último frame, em segundos. Usar dt torna a simulação independente do frame rate: o mesmo comportamento a 30 FPS e a 120 FPS.
# Every frame:vy += GRAVITY * dt # Gravity accelerates the fally += vy * dt # Velocity moves the position
O Physics Update Loop
A physics de plataformas se baseia em duas equações fundamentais aplicadas a cada frame:
dt (delta time) é o tempo decorrido desde o último frame, em segundos. Usar dt torna a simulação independente do frame rate: o mesmo comportamento a 30 FPS e a 120 FPS.
# Every frame:vy += GRAVITY * dt # Gravity accelerates the fally += vy * dt # Velocity moves the position
Importante
Sempre multiplique forças e velocidades por dt. Sem isso, seu jogo vai rodar em velocidades diferentes dependendo do hardware.
Gravity
Gravity é simplesmente uma aceleração constante para baixo aplicada a cada frame:
Um salto é simplesmente uma velocidade instantânea para cima aplicada quando o jogador está no chão:
JUMP_VY =-460.0# Negative = upward (Y grows downward in Pygame)if player_pressed_jump and on_ground: vy = JUMP_VY
Variable Jump Height
Segure o botão de pulo para um salto mais alto; solte antes para um salto curto. O truque: reduzir a gravity enquanto o botão está pressionado e o jogador ainda está subindo.
JUMP_FRAMES_MAX =20# Max frames of reduced gravityJUMP_FRAMES_MIN =6# Must hold at least this longif jumping: jump_frames +=1ifnot keys[K_SPACE] and jump_frames > JUMP_FRAMES_MIN: jumping =False# Cut jump shortif jump_frames >= JUMP_FRAMES_MAX: jumping =Falsegrav_mult =0.4if jumping else1.0# Reduced gravity = higher jumpvy += GRAVITY * grav_mult * dt
Coyote Time
O período de graça do coyote time permite que o jogador pule durante alguns frames depois de andar para fora de uma plataforma — parece justo e responsivo:
Valores de Coyote time e jump buffer
=============================================
Coyote frames : 8 frames = 133 ms
Buffer frames : 10 frames = 167 ms
Por que isso importa:
Sem coyote: andar para fora da borda → não é possível pular (parece injusto)
Com coyote: 8 frames de graça → parece responsive
Sem buffer: pressionar pular 1 frame antes → nada acontece
Com buffer: input mantido por 10 frames → dispara automaticamente ao aterrissar
Collision Resolution
O padrão mais confiável: mova-se em um eixo, resolva a collision, e depois mova-se no outro.
# Step 1: move horizontallyx += vx * dtfor platform in platforms:if player_rect.colliderect(platform.rect):if vx >0: x = platform.rect.left - player_w # Pushed leftif vx <0: x = platform.rect.right # Pushed right vx =0# Step 2: move verticallyy += vy * dton_ground =Falsefor platform in platforms:if player_rect.colliderect(platform.rect):if vy >0: # Falling y = platform.rect.top - player_h vy =0 on_ground =Trueelif vy <0: # Rising y = platform.rect.bottom vy =0
Collision Resolution
O padrão mais confiável: mova-se em um eixo, resolva a collision, e depois mova-se no outro.
# Step 1: move horizontallyx += vx * dtfor platform in platforms:if player_rect.colliderect(platform.rect):if vx >0: x = platform.rect.left - player_w # Pushed leftif vx <0: x = platform.rect.right # Pushed right vx =0# Step 2: move verticallyy += vy * dton_ground =Falsefor platform in platforms:if player_rect.colliderect(platform.rect):if vy >0: # Falling y = platform.rect.top - player_h vy =0 on_ground =Trueelif vy <0: # Rising y = platform.rect.bottom vy =0
Dica
Sempre resolva X e Y separadamente. Resolver os dois juntos faz o jogador atravessar cantos e ficar preso nas paredes de forma incorreta.
One-Way Platforms
Sólidas apenas por cima — o jogador pode atravessá-las pulando por baixo:
for platform in one_way_platforms:if player_rect.colliderect(platform.rect):# Only resolve if falling AND was above the platform top last frameif vy >0and (prev_y + player_h) <= platform.rect.top +4: y = platform.rect.top - player_h vy =0 on_ground =True# If rising: ignore (pass through)
Moving Platforms — Velocity Inheritance
Quando o jogador está parado sobre uma plataforma em movimento, some a velocidade da plataforma ao jogador:
class MovingPlatform:def update(self, dt): prev_x =self.rect.xself.rect.x +=self.direction *self.speed * dtself.vel_x = (self.rect.x - prev_x) / dt # Derived velocity# In player update:if on_ground and standing_on: x += standing_on.vel_x * dt # Inherit platform movement
Acceleration-Based Movement
A velocidade instantânea parece rígida. A aceleração dá peso e momentum ao movimento:
MOVE_ACCEL =1800.0# px/s² — ground accelerationAIR_ACCEL =900.0# Less control in the airMAX_VX =240.0accel = MOVE_ACCEL if on_ground else AIR_ACCELif pressing_right: vx += (MAX_VX - vx) * (accel / MAX_VX) * dt # Accelerate toward targetelse: vx -= math.copysign(MOVE_ACCEL * dt, vx) # Decelerate
Acceleration-Based Movement
A velocidade instantânea parece rígida. A aceleração dá peso e momentum ao movimento:
MOVE_ACCEL =1800.0# px/s² — ground accelerationAIR_ACCEL =900.0# Less control in the airMAX_VX =240.0accel = MOVE_ACCEL if on_ground else AIR_ACCELif pressing_right: vx += (MAX_VX - vx) * (accel / MAX_VX) * dt # Accelerate toward targetelse: vx -= math.copysign(MOVE_ACCEL * dt, vx) # Decelerate
Velocity profile em 30 frames
Frame Instantânea Accelerated
----------------------------------
0 240.0 30.0
5 240.0 132.3
10 240.0 184.8
15 240.0 211.7
20 240.0 225.5
25 240.0 232.5
O movimento Accelerated alcança max speed de forma gradual → parece weighted
Physics — Resumo
velocity += acceleration × dt e depois position += velocity × dt — sempre use dt
Gravity = aceleração constante para baixo + limite de terminal velocity
Variable jump = reduzir a gravity enquanto o botão de pulo está pressionado
Coyote time = período de graça de salto (8 frames) depois de andar para fora de uma plataforma
Jump buffering = lembrar o input de salto (10 frames) antes de aterrissar
Resolução separada de X e Y para evitar que o jogador atravesse cantos
One-way platforms: resolver a collision apenas ao cair e estar acima da borda superior
Moving platforms: derivar vel_x a partir do delta de posição, herdar no jogador