Плавность при любой частоте кадров
Часть раздела Звук и игровые ощущения путешествия по Pygame на Coddy. Урок 19 из 23.
x += (target_x - x) * 0.1 перемещает на десятую часть пути за кадр. При 60 кадрах в секунду это 60 шагов в секунду; на медленном компьютере, который справляется только с 30 кадрами, это 30 шагов, и кругу требуется вдвое больше времени, чтобы добраться до цели. Нажми F, чтобы увидеть это: игра работает со скоростью 30 кадров в секунду, и всё движется медленнее.
Решение — измерять время. clock.tick() возвращает количество миллисекунд с момента предыдущего кадра. Разделив это значение на 1000, получим dt — длительность кадра в секундах: около 0.017 при 60 кадрах в секунду и 0.033 при 30.
dt = 1 / 60 # перед циклом
...
step = min(1, 6 * dt)
x += (target_x - x) * step
...
dt = clock.tick(fps) / 1000За более длинный кадр делается больший шаг, поэтому круг проходит то же расстояние за то же время при любой частоте кадров. Число 6 задаёт скорость: при 60 кадрах в секунду 6 * dt равно примерно 0.1 — той же десятой части, что и раньше. min(1, ...) не позволяет за очень длинный кадр проскочить мимо цели.
То же правило относится и к обычному перемещению: player_x += 240 * dt означает 240 пикселей в секунду при любой частоте кадров. В большинстве небольших игр частоту кадров фиксируют с помощью tick(60), и тогда это не требуется; используй dt, если частота может меняться.
Задание
СреднеКлавиша F переключает игру между 60 и 30 кадрами в секунду. Сделай так, чтобы сглаживание занимало одинаковое время при обоих значениях:
- Перед циклом задай
dt = 1 / 60. - В конце цикла задай
dt = clock.tick(fps) / 1000. - Перемещай объект с помощью
step = min(1, 6 * dt)вместо 0.1:x += (target_x - x) * step, и то же самое дляy.
Попробуйте сами
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()
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Звук и игровые ощущения
4Тряска экрана и вспышка при попадании
Тряска экранаВспышка при попаданииОстановка при попаданииСоблюдайте умеренность5Плавное движение
Движение к целиПлавная камераРезкий эффект при подбореПлавность при любой частоте кадров6Доведём «Звёздный патруль» до блеска
Звуки «Звёздного патруля»Искры при каждом попаданииПотренируйтесь самостоятельно: Песочница Pygame