Menu
CoddyTech

Mượt mà ở mọi tốc độ khung hình

Thuộc phần Âm thanh và cảm giác chơi trong hành trình Pygame của Coddy. Bài học 19 trên 23.

x += (target_x - x) * 0.1 di chuyển một phần mười quãng đường mỗi khung hình. Ở tốc độ 60 khung hình mỗi giây, đó là 60 bước mỗi giây; trên máy tính chậm chỉ chạy được 30 khung hình, con số này chỉ còn 30 bước, và vòng tròn mất gấp đôi thời gian để đến nơi. Nhấn F để xem: trò chơi chạy ở tốc độ 30 khung hình mỗi giây và mọi thứ chuyển động chậm hơn.

Cách khắc phục là đo thời gian. clock.tick() trả về số mili giây kể từ khung hình trước. Chia cho 1000 sẽ ra dt, độ dài của khung hình tính bằng giây: khoảng 0.017 ở tốc độ 60 khung hình mỗi giây, 0.033 ở tốc độ 30.

dt = 1 / 60          # trước vòng lặp
...
step = min(1, 6 * dt)
x += (target_x - x) * step
...
dt = clock.tick(fps) / 1000

Khung hình dài hơn sẽ tạo ra bước di chuyển lớn hơn, nhờ đó vòng tròn di chuyển cùng một quãng đường trong cùng một khoảng thời gian ở mọi tốc độ khung hình. Số 6 thiết lập tốc độ: ở tốc độ 60 khung hình mỗi giây, 6 * dt xấp xỉ 0.1, bằng một phần mười như trước. min(1, ...) ngăn khung hình quá dài khiến vòng tròn di chuyển vượt qua đích.

Điều tương tự cũng áp dụng cho chuyển động thông thường: player_x += 240 * dt tương đương 240 pixel mỗi giây, bất kể tốc độ khung hình. Hầu hết trò chơi nhỏ đều cố định tốc độ bằng tick(60) nên không cần cách này; hãy dùng dt khi tốc độ có thể thay đổi.

challenge icon

Thử thách

Trung bình

Giữ cho chuyển động giảm tốc mất cùng một khoảng thời gian ở mọi tốc độ khung hình.

  1. Đặt dt = 1 / 60 trước vòng lặp.
  2. Ở cuối vòng lặp, đặt dt = clock.tick(fps) / 1000.
  3. Trong cả hai dòng giảm tốc, dùng step = min(1, 6 * dt) thay cho 0.1: x += (target_x - x) * step.

Nhấn Chơi, nhấp chuột, rồi nhấn F và nhấp chuột: chuyển động lướt mất cùng một khoảng thời gian. Sau đó nhấn Kiểm tra.

Tự làm thử

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()
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Âm thanh và cảm giác chơi

Tự luyện tập: Playground Pygame