Что такое игровой цикл

Игра — это не магия, а цикл, который крутится 60 раз в секунду. Разберёмся, из чего он состоит, и почему именно это знание делает тебя автором игр, а не зрителем.
Суть: любая 2D-игра — это бесконечный цикл «обработай ввод → обнови мир → нарисуй кадр», повторяющийся десятки раз в секунду. Pygame даёт окно, события и холст, всё остальное — твоя логика.

Ты наверняка проходил десятки игр. Прыгал через пропасти, стрелял по астероидам, собирал монетки. И почти наверняка думал, что внутри творится что-то невероятно сложное. Хорошая новость: внутри почти каждой 2D-игры лежит одна простая идея — игровой цикл. Компьютер не «понимает» игру целиком. Он просто очень быстро и очень много раз повторяет три действия: смотрит, что нажал игрок, чуть-чуть двигает мир вперёд и перерисовывает картинку. Делает он это так быстро — 60 раз в секунду — что наш глаз видит плавное движение, а не отдельные кадры.

Pygame (а точнее его современная версия pygame-ce, community edition) — это библиотека, которая берёт на себя самую муторную часть: открыть окно, поймать нажатия клавиш, нарисовать прямоугольник или картинку, проиграть звук. Всё это называют «движком» в кавычках, потому что настоящий движок логики ты пишешь сам. И это здорово: ты управляешь каждым пикселем и каждым правилом своей игры.

Зачем начинать именно с цикла, а не с красивых спрайтов? Потому что без понимания цикла любая графика — просто статичная картинка. Цикл — это сердце, которое заставляет картинку оживать. Освоишь его — и всё остальное (движение, коллизии, счёт) ляжет внутрь этого же ритма.

Как работает под капотом

Представь, что ты сам — компьютер, и тебе дали блокнот-флипбук. На каждой странице ты рисуешь героя чуть правее, чем на предыдущей. Перелистываешь быстро — герой «бежит». Игровой цикл — это и есть рисование страниц флипбука в реальном времени. Каждая итерация цикла = одна страница = один кадр.

        ИГРОВОЙ ЦИКЛ (один оборот = один кадр)

   +-----------------------------------------------+
   |                                               |
   v                                               |
  [ 1. СОБЫТИЯ ] --> что нажал игрок? закрыл окно? |
        |                                          |
        v                                          |
  [ 2. ОБНОВЛЕНИЕ ] --> подвинь героя, врагов,     |
        |              посчитай очки, проверь      |
        v              столкновения                |
  [ 3. ОТРИСОВКА ] --> залей фон, нарисуй всё      |
        |              поверх                      |
        v                                          |
  [ 4. ПОКАЗ КАДРА ] --> flip() + ждём до 60 FPS --+

Эти четыре шага повторяются снова и снова, пока игрок не закроет окно. Любая команда вроде «герой прыгнул» — это просто изменение одного числа на шаге 2, которое станет видно на шаге 3.

Вот как выглядит минимальный каркас на pygame. Запустить в браузере его нельзя (pygame рисует в настоящем окне), но прочитать и понять — обязательно:

import pygame

pygame.init()                         # включаем подсистемы
screen = pygame.display.set_mode((800, 600))  # окно 800x600
clock = pygame.time.Clock()           # часы для контроля FPS
running = True

while running:                        # <-- игровой цикл
    for event in pygame.event.get():  # 1. события
        if event.type == pygame.QUIT:
            running = False

    # 2. обновление мира (пока пусто)

    screen.fill((30, 30, 40))         # 3. заливаем фон
    pygame.display.flip()             # 4. показываем кадр
    clock.tick(60)                    # держим 60 кадров/сек

pygame.quit()

А вот доказательство, что «цикл» — это обычный код на Python, который ты можешь запустить прямо сейчас без всякой графики. Здесь мы крутим цикл 5 «кадров» и печатаем номер каждого. Это тот же ритм, только вместо рисунка — print. Попробуй сам:

# Имитация игрового цикла без графики
frame = 0
running = True
while running:
    frame += 1
    print("Кадр номер:", frame)
    if frame >= 5:        # обычно цикл рвёт игрок, тут — счётчик
        running = False
print("Игра закрыта на кадре", frame)

Зачем именно так, а не иначе

Может возникнуть вопрос: почему нельзя нарисовать картинку один раз и просто двигать её, как объект на слайде? Потому что экран компьютера не «помнит» предыдущий кадр так, как нам нужно. Каждый кадр мы как бы вытираем доску начисто (заливка фоном) и рисуем всё заново в новых позициях. Это называется immediate mode — «немедленный режим» отрисовки. Звучит расточительно, но именно полная перерисовка делает движение абсолютно гибким: герой может прыгать, мигать, менять размер, и нам не нужно думать, как «стереть» его прежнее положение. Заливка фоном стирает всё разом.

Ещё одна тонкость — порядок шагов нельзя менять местами. Если обработать события ПОСЛЕ отрисовки, игрок будет видеть мир на кадр устаревшим: нажал клавишу, а реакция появилась только через кадр. Если рисовать ДО обновления, увидишь старые позиции. Поэтому порядок «события → обновление → отрисовка → показ» — это не случайность, а единственно правильная последовательность, выверенная десятилетиями геймдева. Запомни её как таблицу умножения, и большая часть загадочных багов с «запаздыванием» обойдёт тебя стороной.

Частые ошибки

  • Забыть clock.tick() — цикл крутится с бешеной скоростью, процессор греется, а игра летит непредсказуемо быстро.
  • Не обрабатывать QUIT — окно невозможно закрыть крестиком, приходится убивать программу.
  • Рисовать до заливки фона — старые кадры остаются на экране, получается «смазанный след».

Best practices

  • Держи структуру цикла всегда одинаковой: события → обновление → отрисовка → flip. Не смешивай эти этапы.
  • Сразу заводи clock и tick(60), даже в самом первом наброске.
  • Используй pygame-ce: pip install pygame-ce — это активно развиваемая версия с теми же командами.

Итог: игра — это цикл из четырёх шагов, который крутится 60 раз в секунду. Ты уже знаешь его наизусть. Дальше мы наполним каждый шаг настоящим содержимым.

Проверьте себя
1. Сколько примерно раз в секунду обычно крутится игровой цикл в 2D-игре?
A1 раз
B10 раз
C60 раз
D6000 раз
2. Что делает вызов clock.tick(60) внутри цикла?
AРисует 60 спрайтов
BОграничивает цикл до 60 кадров в секунду
CСоздаёт 60 окон
DЗагружает 60 звуков