Изучаем PyGame на простых примерах

16 июля 2025

PyGame — это библиотека для создания мультимедийных приложений на Python. Зачастую позиционируется, как простой движок для создания двухмерных игр. Скажем, вы хотите написать свой сокобан или пошаговую стратегию в стиле Героев III, но не хотите учить Unity или Unreal Engine. Тогда PyGame создан для вас. Впрочем, как мы скоро убедимся, PyGame пригоден не только для игр.

В качестве примера рассмотрим демку, рисующую синусоиду:

#!/usr/bin/env python3

import pygame
from math import sin, pi

width, height = 1024, 768
blue = (0, 0, 100)
white = (255, 255, 255)

amplitude = height // 2 - 50
freq = 4
phase = 0
show_fps = True

pygame.init()
font = pygame.font.Font(None, 36)
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Sine Wave Demo")

running = True
clock = pygame.time.Clock()

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_q:
                running = False
            elif event.key == pygame.K_f:
                show_fps = not show_fps

    screen.fill(blue)

    points = []
    for x in range(width):
        s = sin(2 * pi * freq * x / width + phase)
        y = height // 2 + amplitude * s
        points.append((x, y))

    pygame.draw.lines(screen, white, False, points, 2)

    if show_fps:
        fps = clock.get_fps()
        fps_text = font.render(f"FPS: {fps:.1f}", True, white)
        screen.blit(fps_text, (15, 15))

    pygame.display.flip()

    phase += 0.1
    clock.tick(60)

pygame.quit()

Так выглядит результат:

Демка с синусоидой на PyGame

Здесь фаза синусоиды является функцией от времени, что создает иллюзию плавного движения графика справа налево. В левом верхнем углу выводится частота кадров в секунду.

Вот как это работает:

width, height = 1024, 768

pygame.init()
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Sine Wave Demo")

Здесь происходит инициализация библиотеки. Затем создается окно указанной ширины и высоты в пикселях. Окну присваивается заголовок. Переменная screen является экземпляром класса Surface. Это один из ключевых классов в PyGame, представляющий собой двухмерное изображение. Например, двумерный «холст», с которым мы работаем, а также спрайты — все это экземпляры Surface.

clock = pygame.time.Clock()

while running:
    # ...
    fps = clock.get_fps()
    # ...
    clock.tick(60)

Класс Clock, как следует из названия, представляет собой внутриигровые часы. Метод tick должен вызываться после отрисовки каждого кадра. Он вычисляет время, прошедшее с последнего вызова, и из него определяет частоту кадров в секунду (FPS). С помощью опционального аргумента можно сообщить целевую частоту кадров. Тогда tick будет создавать задержку, чтобы FPS не превышал желаемый. Метод get_fps возвращает реально вычисленный FPS.

for event in pygame.event.get():
    if event.type == pygame.QUIT:
        # ...
    elif event.type == pygame.KEYDOWN:
        if event.key == pygame.K_q:
            # ...
        elif event.key == pygame.K_f:
            # ...

Вызов pygame.event.get() возвращает список новых событий, таких, как нажатия клавиш и закрытие окна программы. Итерируемся по списку, обрабатываем.

blue = (0, 0, 100)
white = (255, 255, 255)

# ...

screen.fill(blue)

points = []
for x in range(width):
    s = sin(2 * pi * freq * x / width + phase)
    y = height // 2 + amplitude * s
    points.append((x, y))

pygame.draw.lines(screen, white, False, points, 2)

Сначала заполняем холст синим цветом. Далее создаем список координат точек, принадлежащих синусоиде. С помощью pygame.draw.lines соединяем эти точки белыми линиями толщиной в два пикселя. Помимо линий pygame.draw умеет рисовать прямоугольники, эллипсы, и так далее.

font = pygame.font.Font(None, 36)
# ...
fps_text = font.render(f"FPS: {fps:.1f}", True, white)
screen.blit(fps_text, (15, 15))

Здесь мы создаем дэфолтный шрифт высотой 36 пикселей. Далее при помощи метода render создается новый Surface, на котором выведен текущий FPS. Затем с помощью метода Surface.blit выводим его на холсте в левом верхнем углу. Название blit является акронимом к bit block transfer. Термин blitting является устоявшимся в мире компьютерной графики.

pygame.display.flip()

Обновляем изображение на дисплее / холсте.

pygame.quit()

Деинициализация модуля, метод обратный к init. Вызывать его не обязательно. Все ресурсы в любом случае будут освобождены при завершении скрипта.

Примечание: На момент написания этих строк в PyGame имеется баг, из-за которого pygame.quit иногда повисает на Linux. Есть несколько воркэраундов, наиболее универсальный из которых — завершать скрипт через os._exit(0).

Пусть пример получился и не самым захватывающим, зато он демонстрирует ~85% всех возможностей PyGame. Это действительно очень простая библиотека. По крайней мере, в плане ее интерфейса. Если хотите более интересный пример, могу предложить классическую демку, рисующую «плазму»:

Демка с плазмой на PyGame

Код этой демки доступен здесь. Плазма красиво переливается разными цветами, однако показать это в блоге у меня, увы, не получится. Вам придется установить PyGame и запустить скрипт, чтобы увидеть результат в динамике. Качественно код плазмы не намного сложнее кода синусоиды. Заинтересованным читателям предлагается изучить скрипт самостоятельно. Реализована возможность запуска в полноэкранном режиме. А значит, перед нами полноценный скринсейвер.

Плазма примечательна тем, что рисуется попиксельно. При жалении, на PyGame можно написать не слишком динамичную игру, стилизованную под pixel art. При этом, из методов PyGame понадобится лишь Surface.set_at.

Резюмируя, я вижу следующие применения PyGame:

  • Написание 2D игр. Притом, библиотека с одинаковым успехом подойдет как для змейки, так и для какой-нибудь сложной пошаговой стратегии. А может быть, и не только пошаговой;
  • Написание демок. Неплохая подборка примеров доступна здесь. В этих примерах использованы JavaScript и Canvas, но суть та же;
  • Используя Surface.set_at, можно получить опыт, схожий с опытом программирования под ретро-компьютеры, в смысле прямой работы с «видео памятью». Только без ассемблера и без ретро-железа;
  • Эмуляция каких-нибудь ZX Spectrum, БК-0010-01 и Искры 1080 Тарту;
  • Реализация алгоритма Брезенхэма для прямых линий и окружностей. Данный алгоритм вполне себе актуален на тех же микроконтроллерах;
  • Рисование каких-нибудь кривых Гильберта и Серпинского. Их часто используют для объяснения рекурсии начинающим программистам;
  • Можно законтрибьютить в сам PyGame, если вам нравиться работать над игровыми движками или чем-то, что их напоминает;

К сожалению, в рамках одного поста не получилось рассмотреть абсолютно все возможности PyGame. Например, за кадром остались работа со звуком и мышью. Справка по всем доступным классам и их методам, а также большое количество примеров, доступны на официальном сайте PyGame. Также есть неплохая книга «Beginning Python Games Development With PyGame, Second Edition», авторы Harrison Kinsley и Will McGugan.

Метки: .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.