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

Управление временем в PyGame

6 min read Разработка игр Обновлено 05 Jan 2026
Управление временем в PyGame
Управление временем в PyGame

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

Зачем контролировать время в игре

Видеоигры — это не только графика и звук, но и согласованная логика, зависящая от времени: скорость движения, длительность баффов, интервалы появления врагов, анимация и физика. Без управления временем поведение игры будет зависеть от производительности устройства: на быстром ПК всё станет слишком резким, а на слабом — вялым.

Определение: delta-time — это время, прошедшее с предыдущего кадра, обычно в миллисекундах или секундах. Используется, чтобы масштабировать обновления игры независимо от FPS.

Создание простой игры (пример)

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

Сначала установите 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)

Главный игровой цикл с движением прямоугольника:

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()

Выход примера — простой экран с квадратом игрока:

Экран простого PyGame: квадратный игрок

Примечание: в этом простом примере скорость привязана к частоте вызова цикла. На разных машинах прямоугольник будет двигаться с разной скоростью.

Модуль pygame.time — обзор

PyGame предоставляет модуль pygame.time для контроля времени и тайминга. Основные инструменты:

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

Создание Clock для отслеживания времени

Создание объекта Clock:

clock = pygame.time.Clock()

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

  • get_time(): возвращает длительность последнего кадра в миллисекундах.
  • tick(fps): ограничивает цикл до заданного FPS (по умолчанию 60). Например, clock.tick(30) — ограничит 30 FPS.
  • get_fps(): возвращает текущую оценку FPS.
  • tick_busy_loop(fps): похож на tick, но использует busy-wait для более точного ожидания.

Пример отображения времени и FPS (логика вывода остаётся в основном тексте; строки для вывода вы можете вставить в свой цикл):

# После обновления clock в конце цикла
time = clock.get_time()
font = pygame.font.SysFont('Arial', 18)  
text = font.render('Time taken: {} ms'.format(time), True, (0, 0, 0))  
screen.blit(text, (0, 0))

fps = clock.get_fps()
text2 = font.render('FPS: {}'.format(fps), True, (0, 0, 0))
screen.blit(text2, (0, 20))

Важно: get_time() возвращает длительность последнего кадра в миллисекундах, а get_fps() — усреднённую оценку FPS.

Использование get_ticks

Функция get_ticks() возвращает количество миллисекунд с момента вызова pygame.init():

time_elapsed = pygame.time.get_ticks()

Вы можете использовать это значение для управления временем эффектов и таймеров. Пример отображения:

font = pygame.font.SysFont('Arial', 18)  
text = font.render('Time Elapsed: {} ms'.format(time_elapsed), True, (0, 0, 0))  
screen.blit(text, (0, 40))

Пример использования: бафф, действующий 5000 миллисекунд (5 секунд):

# логика: если прошло больше 5000 мс — увеличиваем скорость
if time_elapsed / 5000 > 0:  
   rect.x += 5

Этот фрагмент демонстративен; в реальном коде проверяйте абсолютные интервалы и состояние баффа (см. рекомендации ниже).

Выход этого примера — экран с текстом времени и игровым объектом:

Игровой экран с текстом прошедшего времени и игровым объектом

wait и delay — приостановка выполнения

Обе функции задерживают дальнейшее выполнение, но отличаются поведением:

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

Примеры (1000 миллисекунд = 1 секунда):

pygame.time.wait(1000)
# или
pygame.time.delay(1000)

Используйте wait в фоновых задачах и при необходимости экономии ресурсов; delay полезен, когда нужна точность на миллисекундном уровне.

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

set_timer позволяет отправлять пользовательские события в очередь событий через заданный интервал:

pygame.time.set_timer(CUSTOM_EVENT, 1000)

Это создаст событие CUSTOM_EVENT каждые 1000 миллисекунд. В обработчике событий вы можете реагировать на него:

if event.type == CUSTOM_EVENT:
   rect.y += 20

set_timer удобен для периодов появления врагов, смены фаз игры или временных эффектов.

Практические рекомендации и шаблоны

Ниже собраны подходы, которые помогут выбрать правильную модель управления временем для вашей игры.

Модель 1 — Переменный шаг (variable timestep)

Обновление позиции с использованием delta-time (рекомендуется для простых игр):

  • Получаем delta в секундах: dt = clock.get_time() / 1000.0
  • Смещаем объект: x += speed * dt

Плюсы: простота, логика масштабируется под FPS. Минусы: нестабильная физика при колебаниях FPS.

Пример паттерна:

# внутри цикла
dt = clock.get_time() / 1000.0  # секунды
rect.x += int(speed * dt)
clock.tick(60)

Модель 2 — Фиксированный шаг (fixed timestep)

Подходит для детерминированной физики (например, симуляции):

  • Выбираем фиксированный шаг, например 1/60 секунды.
  • Накопливаем время и делаем несколько обновлений логики, если прошло много времени.
  • Рендерим один раз.

Краткий пример:

FIXED_DT = 1.0 / 60.0
accumulator = 0.0
prev_time = time.perf_counter()
while running:
    now = time.perf_counter()
    frame_time = now - prev_time
    prev_time = now
    accumulator += frame_time
    while accumulator >= FIXED_DT:
        update_game_logic(FIXED_DT)
        accumulator -= FIXED_DT
    render()

Плюсы: стабильная физика, предсказуемость. Минусы: чуть более сложная реализация, возможна дополнительная задержка ввода.

Когда использовать tick_busy_loop

tick_busy_loop полезен, если требуется сверхточное ограничение FPS (например, для синхронизации внешних устройств), но он потребляет CPU. Для большинства игр достаточно обычного tick.

Когда подходы не работают — типичные ошибки и контрпримеры

  • Использование rect.x += 1 без учета delta: перемещение зависит от FPS, что приводит к разной скорости на разных машинах.
  • Простой отрезок wait(1000) внутри игрового цикла блокирует обработку событий (включая закрытие окна) — используйте таймеры либо отдельные потоки для фоновых пауз.
  • set_timer используется для периодических задач, но если обработка события занимает больше времени, чем интервал, события начнут накапливаться.
  • Злоупотребление tick_busy_loop на мобильных устройствах приводит к сильному нагреву и быстрой разряду батареи.

Альтернативные инструменты и подходы

  • Использовать модуль time (time.perf_counter) для измерения точного времени вне PyGame.
  • Для сетевых игр — синхронизировать игровую логику через фиксированный шаг и подтверждения от сервера.
  • Для сложной физики — использовать внешние движки (Box2D, Pymunk) с фиксированным шагом физики.

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

  1. Определите требования: нужна ли предсказуемая физика? Какой допустимый разброс FPS? Есть ли таймеры/баффы?
  2. Выберите стратегию: variable timestep для простых аркад, fixed timestep для физики.
  3. Внедрите Clock и delta-time: в каждом кадре вычисляйте dt и масштабируйте движение.
  4. Тестируйте на разных FPS (например, 30, 60, 144) и на худших устройствах.
  5. Используйте set_timer для периодических событий и избегайте wait/delay внутри основного цикла.
  6. Документируйте в коде, где используются временные допущения.

Шпаргалка: полезные вызовы

  • clock = pygame.time.Clock()
  • dt_ms = clock.get_time() # миллисекунды
  • dt_s = dt_ms / 1000.0 # секунды
  • clock.tick(60) # ограничить до 60 FPS
  • fps = clock.get_fps()
  • t = pygame.time.get_ticks() # миллисекунды с запуска
  • pygame.time.set_timer(MY_EVENT, 2000) # событие каждые 2 секунды

Роли и чек-листы

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

  • Внедрить Clock и dt-вычисления.
  • Выбрать fixed/variable timestep.
  • Избегать блокирующих wait внутри основного цикла.
  • Тестировать логические интервалы (баффы, таймеры).

QA/Тестировщик:

  • Проверить поведение при 30/60/120+ FPS.
  • Проверить таймеры set_timer при нагрузке.
  • Поймать накопление событий и race-conditions.

Дизайнер/Геймдизайнер:

  • Указать требуемую длительность эффектов в секундах.
  • Определить допустимую вариативность отклика управления.

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

  • Персонаж движется с одинаковой игровой скоростью при 30, 60 и 144 FPS (допуск ±5%).
  • Временные баффы действуют указанное количество секунд независимо от FPS.
  • set_timer с интервалом 1000 мс генерирует событие не реже, чем раз в секунду при нормальной нагрузке.
  • Игра не блокируется функциями wait/delay в основном потоке дольше, чем на одно целевое обновление.

Тест-кейсы

  1. Запустить игру с ограничением 30 FPS и измерить пройденное расстояние за 10 секунд; повторить при 60 FPS — результаты должны совпадать.
  2. Активировать бафф на 5 секунд и убедиться, что он длится именно 5 секунд при разных FPS.
  3. Установить set_timer с 500 мс и замерить число срабатываний за 10 секунд.

Принятие решения: выбор стратегии (Mermaid)

flowchart TD
  A[Требуется стабильная физика?] -->|Да| B[Fixed timestep]
  A -->|Нет| C[Variable timestep]
  B --> D{Нужна ли интерполяция?
}
  D -->|Да| E[Добавить интерполяцию при рендере]
  D -->|Нет| F[Обновлять и рендерить как есть]
  C --> G{Частые скачки FPS?}
  G -->|Да| H[Усреднить dt, ограничить max dt]
  G -->|Нет| I[dt * скорость — нормально]

Глоссарий (в одну строку)

  • delta-time: время с предыдущего кадра; нужен для масштабирования скоростей.
  • FPS: frames per second — частота кадров.
  • tick: метод Clock для ограничения FPS.
  • get_ticks: миллисекунды с момента инициализации PyGame.

Короткое резюме

Контроль времени в PyGame — базовый навык для стабильной и предсказуемой игры. Для простых проектов достаточно pygame.time.Clock и delta-time. Для сложной физики используйте фиксированный шаг. set_timer удобен для повторяющихся событий, а wait/delay — для редких и некритичных пауз. Тестируйте поведение на разных конфигурациях и документируйте выбранную стратегию.

Важно: избегайте блокировки основного цикла и учитывайте энергопотребление на целевых устройствах.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

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

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство