Гид по технологиям

Управление временем в PyGame: практическое руководство

6 min read Разработка игр Обновлено 26 Nov 2025
Управление временем в PyGame — практическое руководство
Управление временем в PyGame — практическое руководство

Вид сверху на несколько ноутбуков, лежащих на столе

Вступление

Управление временем — одна из базовых задач при разработке игр. Неправильно организованный цикл кадра приводит к дерганой анимации, рассинхрону логики и неудовлетворительному опыту пользователя. PyGame предоставляет готовые средства для контроля времени: Clock, get_ticks, wait, delay и set_timer. В этом руководстве вы найдёте практические примеры, сравнения и советы по оптимизации.

Быстрая простая игра: движущийся прямоугольник

Ниже — минимальный пример игры, где прямоугольник непрерывно движется вправо и «оборачивается» в начале экрана. Убедитесь, что PyGame установлен:

pip install pygame

Пример кода:

import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
background = pygame.Surface(screen.get_size())
background.fill((255, 255, 255))
rect = pygame.Rect(0, 0, 20, 20)
clock = pygame.time.Clock()

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    rect.x += 1
    if rect.x > 780:
        rect.x = 0

    screen.blit(background, (0, 0))
    pygame.draw.rect(screen, (0, 0, 0), rect)
    pygame.display.update()
    clock.tick(60)

pygame.quit()

Пример вывода визуально показан на изображении ниже.

Простой экран PyGame c квадратным игровым объектом

Основы модуля pygame.time

PyGame предоставляет модуль pygame.time с набором инструментов для контроля времени и частоты кадров. Ключевые элементы:

  • Clock — объект для управления и измерения времени кадра.
  • get_ticks() — общее время в миллисекундах с момента инициализации pygame.
  • wait(ms) и delay(ms) — разные способы приостановки выполнения.
  • set_timer(event, ms) — запускает пользовательские события по таймеру.

Как создать объект Clock и зачем он нужен

Clock помогает стабилизировать частоту кадров и измерять временные интервалы между кадрами.

clock = pygame.time.Clock()

Полезные методы Clock:

  • get_time() — длительность последнего кадра в миллисекундах.
  • tick(fps) — ограничивает FPS (например, clock.tick(60)). Возвращает миллисекунды, прошедшие с предыдущего вызова.
  • get_fps() — скользящая оценка текущих FPS.
  • tick_busy_loop(fps) — более точный, но более затратный в CPU вариант.

Пример вывода времени и FPS на экран:

# внутри игрового цикла
ms = clock.get_time()
fps = clock.get_fps()
font = pygame.font.SysFont('Arial', 18)
text1 = font.render(f'Time: {ms} ms', True, (0,0,0))
text2 = font.render(f'FPS: {fps:.1f}', True, (0,0,0))
screen.blit(text1, (0,0))
screen.blit(text2, (0,20))
clock.tick(60)

get_ticks: глобальная временная шкала

get_ticks() возвращает количество миллисекунд с момента pygame.init(). Это удобно для временных триггеров, power-up’ов и конечных интервалов.

time_elapsed = pygame.time.get_ticks()
# пример: ускорение на 5 секунд
if time_elapsed - powerup_start_ms < 5000:
    rect.x += 5

Экран игры с текстом времени и игровым объектом

wait vs delay: в чём разница

  • pygame.time.wait(ms): приостанавливает выполнение и отдаёт процессор другим задачам — экономно по CPU, но менее точное.
  • pygame.time.delay(ms): блокирует поток и старается быть точнее, но использует больше процессорного времени.

Рекомендация: для простых пауз в однопоточных утилитах используйте wait; для циклов, где нужна высокая точность кадра и вы готовы потратить CPU, — delay или tick_busy_loop.

set_timer: события по расписанию

set_timer позволяет запускать кастомные события через регулярные интервалы. Это удобно для врагов, спавна объектов или таймоверов.

CUSTOM_EVENT = pygame.USEREVENT + 1
pygame.time.set_timer(CUSTOM_EVENT, 1000)  # 1000 ms

for event in pygame.event.get():
    if event.type == CUSTOM_EVENT:
        rect.y += 20

Используйте set_timer, когда логика должна запускаться в основном цикле через событие, а не в отдельном потоке.

Практические приёмы и шаблоны использования времени

  1. Универсальная игровая петля с временной нормализацией (frame-independent movement):
# delta в секундах — время, прошедшее с прошлого кадра
delta = clock.tick(60) / 1000.0
speed = 200  # пикселей в секунду
rect.x += speed * delta

Преимущество: движение остаётся плавным при варьирующем FPS.

  1. Таймеры для эффектов с истечением срока:
if powerup_active:
    if pygame.time.get_ticks() - powerup_started_at > 5000:
        powerup_active = False
  1. Использование событий для периодических действий:
  • set_timer для спавна врагов каждые N миллисекунд.
  • отмена таймера: pygame.time.set_timer(CUSTOM_EVENT, 0).

Сравнение: tick, tick_busy_loop, wait, delay

ФункцияТочностьНагрузка CPUРекомендуется для
clock.tick(fps)ХорошаяНизкаяОбычный цикл игры, ограничение FPS
clock.tick_busy_loop(fps)Очень высокаяВысокаяОчень точная синхронизация, критичные тайминги
pygame.time.wait(ms)СредняяНизкаяПростые паузы, демо-режимы
pygame.time.delay(ms)ВысокаяСредняя–ВысокаяКороткие, точные задержки в однопоточном коде

Факто-бокс: ключевые числа

  • 60 FPS ≈ 16.67 ms на кадр
  • 30 FPS ≈ 33.33 ms на кадр
  • get_ticks() возвращает время в миллисекундах
  • set_timer принимает значения в миллисекундах

Ментальные модели и эвристики

  • Модель «delta-first»: всегда рассчитывайте движение и анимацию через delta-время (секунды/мс). Это защищает от проседания FPS.
  • Модель «событие против состояния»: используйте set_timer и события для периодических действий; храните состояния (например, power-up active) и проверяйте их по времени через get_ticks.
  • Эвристика производительности: предпочитайте clock.tick() большинству игр — сочетание точности и экономии CPU.

Когда управление временем даёт сбои и как их обнаружить

Примеры проблем:

  • Лаги при загрузке — tick не поможет, если основной поток загружен длинными синхронными операциями (IO, загрузка текстур).
  • Нестабильная физика при плавающей точке — при больших delta нужно класть лимиты на максимальный delta.
  • Таймеры с set_timer накладываются друг на друга — следите за частотой и отменяйте таймеры при смене состояния.

Диагностика:

  • Логи fps и ms с помощью Clock.get_time/get_fps.
  • Ограничение максимального delta: clamp(delta, 0, 0.1) чтобы избежать прыжков при «заморозке».

Чек-листы по ролям

Разработчик:

  • Использует delta для всех перемещений.
  • Ограничивает максимальный delta.
  • Не запускает тяжёлые операции в основном цикле.

Дизайнер механик:

  • Определяет длительности эффектов в секундах.
  • Тестирует таймеры при разных FPS.

Тестер/QA:

  • Проверяет логику при 30, 60 и 120 FPS.
  • Ищет рассинхрон UI и «серверной» логики в однопользовательском режиме.

Сниппет-структура: «быстрый шпаргалка»

# Инициализация
clock = pygame.time.Clock()
FPS = 60

# В цикле
delta = clock.tick(FPS) / 1000.0  # секунды
# движение с учётом delta
pos += velocity * delta

# проверка таймера
if pygame.time.get_ticks() - start_time > duration_ms:
    end_effect()

# пользовательский таймер
MY_EVENT = pygame.USEREVENT + 2
pygame.time.set_timer(MY_EVENT, 2000)  # каждые 2 секунды

Мини-методология внедрения управления временем

  1. Выделите единицы времени (мс/с) и используйте единый подход по проекту.
  2. Внедрите delta-время для всех перемещений и анимаций.
  3. Используйте set_timer для периодических событий и get_ticks для одноразовых задержек.
  4. Ограничьте максимальный delta и логируйте FPS в режиме отладки.

Критерии приёмки

  • Движение объектов остаётся плавным при изменении FPS от 30 до 120.
  • Таймеры срабатывают с допустимым отклонением (игровая логика не нарушается).
  • При паузе/возобновлении время эффектов корректно сохраняется.

Типичные ошибки и способы их исправления

  • Ошибка: обновление состояния по фиксированной величине пикселей (rect.x += 5) — ведёт к несовместимости с разным FPS. Решение: умножать на delta.
  • Ошибка: длительные блокирующие операции в игровом цикле. Решение: вынести в поток/асинхрон или выполнять в кадрах по частям.

Edge-case галерея

  • Нулевой FPS при перелистывании вкладки ОС: ограничьте максим delta и корректно обрабатывайте паузу.
  • Множественные set_timer на один и тот же EVENT: поддерживайте реестр активных таймеров и очищайте ненужные.

Советы по производительности и безопасности

  • Для точной физики используйте фиксированный шаг симуляции: накапливайте delta и применяйте несколько сим-итераций с фиксированным шагом (например, 1/120 с).
  • Не храните критичные таймеры только в короткоживущих объектах без явной остановки при уничтожении.

Сводка

Управление временем — ключевой навык для стабильных и предсказуемых игр. Clock, get_ticks, set_timer, wait и delay имеют свои сильные и слабые стороны: используйте delta-время для движения, set_timer для регулярных событий и ограничивайте delta, чтобы избежать больших «прыжков» логики. Тестируйте при разных FPS и держите профилирование под рукой.

Важно

  • Для большинства проектов достаточно clock.tick(FPS) и delta-времени.
  • Используйте tick_busy_loop только при необходимости.

Конец руководство

1-строчный глоссарий:

  • delta — время, прошедшее с предыдущего кадра (в секундах)
  • tick — метод объекта Clock для регулировки FPS
  • get_ticks — абсолютное время в миллисекундах с запуска PyGame
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Исправить ошибку «Oops! Something went wrong» в YouTube
Техподдержка

Исправить ошибку «Oops! Something went wrong» в YouTube

Экран входа macOS — настройки и советы
macOS

Экран входа macOS — настройки и советы

Удалить историю Google Bard и отключить её
Конфиденциальность

Удалить историю Google Bard и отключить её

TinyLetter для блогеров: быстро и просто
Email-маркетинг

TinyLetter для блогеров: быстро и просто

Как включить и отключить блокировщик всплывающих окон IE11
браузер

Как включить и отключить блокировщик всплывающих окон IE11

Применение патчей при перемещённых файлах в Git
GIT

Применение патчей при перемещённых файлах в Git