Pygame: первая игра на Python
Python Pygame с нуля: установка, окно, игровой цикл, события клавиатуры и мыши, отрисовка, спрайты и столкновения, FPS через Clock и мини-игра целиком.
Содержание · 11 разделов
- Установка Pygame
- Окно и игровой цикл
- FPS и pygame.time.Clock
- События: клавиатура и мышь
- Отрисовка: фигуры, картинки и текст
- Прямоугольники Rect и столкновения
- Спрайты и группы
- Мини-игра «Лови монеты»
- Частые ошибки
- Окно открывается и сразу закрывается или «не отвечает»
- Ничего не рисуется
- Объект оставляет за собой «шлейф»
- pygame.error: video system not initialized
- pygame.error: No video mode has been set
- FileNotFoundError при загрузке картинки
- Ошибка сборки при pip install pygame
- Частые вопросы
- Почему вместо русских букв выводятся прямоугольники?
- Как сделать задержку, не останавливая игру?
- Как проверять столкновения круглых объектов?
- Как добавить фоновую музыку?
- Как называются клавиши Shift, Ctrl и Tab?
- Итог
Pygame — библиотека Python для 2D-игр: она открывает окно, рисует фигуры и картинки, обрабатывает клавиатуру и мышь, проигрывает звук. Каркас любой игры на Python Pygame укладывается в десяток строк:
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT: # нажали крестик окна
running = False
screen.fill("black")
pygame.display.flip()
clock.tick(60) # не больше 60 кадров в секунду
pygame.quit()
Ниже разберём каждую строку этого каркаса, научимся рисовать, двигать объекты и проверять столкновения, а в конце соберём мини-игру «Лови монеты». Код проверен на pygame 2.6 и Python 3.12.
Установка Pygame
Pygame ставится через pip, лучше в виртуальное окружение:
# Windows
py -m pip install pygame
# macOS / Linux
python3 -m pip install pygame
Проверка — запустите встроенный пример:
python -m pygame.examples.aliens
Если открылось окно с игрой, всё работает. При импорте pygame печатает в консоль строку с версиями pygame, SDL и Python — это нормально.
Важно: версия Python. У pygame 2.6.1 (последний выпуск на октябрь 2026 года) готовые сборки на PyPI есть для Python до 3.13 включительно. На Python 3.14 pip попытается собрать пакет из исходников, и на обычном компьютере это, скорее всего, закончится ошибкой. Выходы: создать окружение на Python 3.13 или поставить форк pygame-ce (
pip install pygame-ce): в версии 2.5.8 у него есть готовые сборки для Python 3.10–3.15.
pygame-ce (Community Edition) — активно развиваемый форк с тем же API: импортируется тоже как import pygame, и все примеры из статьи в нём работают. Не ставьте pygame и pygame-ce в одно окружение — они конфликтуют. Общие сведения о pip — в руководстве по pip.
Окно и игровой цикл
Разберём каркас из начала статьи:
pygame.init()инициализирует модули pygame (графику, шрифты, звук).pygame.display.set_mode((640, 480))создаёт окно 640×480 и возвращает поверхность (Surface) — холст, на котором вы рисуете. Координата(0, 0)— левый верхний угол, ось Y направлена вниз.while running:— игровой цикл. Одна итерация — один кадр. Каждый кадр делает три вещи: обрабатывает события, обновляет состояние игры, рисует.pygame.display.flip()показывает нарисованный кадр на экране. Пока его не вызвать, изменений не видно.pygame.quit()закрывает окно и освобождает ресурсы.
Заголовок окна задаётся так: pygame.display.set_caption("Моя игра"). Если в цикле не обрабатывать события, Windows через несколько секунд пометит окно как «не отвечает», поэтому pygame.event.get() вызывается в каждом кадре. Если вы ещё не уверенно чувствуете себя с циклами, загляните в статью про цикл while.
FPS и pygame.time.Clock
clock.tick(60) делает паузу так, чтобы цикл выполнялся не чаще 60 раз в секунду, и возвращает время в миллисекундах, прошедшее с прошлого вызова. Без tick() цикл крутится с максимальной скоростью и загружает процессор впустую.
Скорость движения лучше считать в пикселях в секунду, умножая на время кадра dt. Тогда объект движется одинаково быстро и при 30, и при 144 кадрах в секунду:
speed = 300 # пикселей в секунду
x = 0
while running:
dt = clock.tick(60) / 1000 # секунды с прошлого кадра
x += speed * dt
Текущую частоту кадров показывает clock.get_fps() — удобно для отладки.
События: клавиатура и мышь
Все действия пользователя приходят в очередь событий. У каждого события есть type, а у клавиатурных и мышиных — дополнительные атрибуты:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN: # клавиша нажата
if event.key == pygame.K_ESCAPE:
running = False
elif event.key == pygame.K_SPACE:
print("Прыжок!")
elif event.type == pygame.MOUSEBUTTONDOWN: # клик мышью
print("Клик в точке", event.pos, "кнопкой", event.button)
События подходят для однократных действий: выстрел, прыжок, пауза. Для непрерывного движения, пока клавиша зажата, удобнее опрашивать состояние клавиатуры в каждом кадре:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= speed * dt
if keys[pygame.K_RIGHT]:
x += speed * dt
Позицию курсора возвращает pygame.mouse.get_pos().
Отрисовка: фигуры, картинки и текст
Каждый кадр обычно рисуется с нуля: сначала заливка фона, затем объекты, затем flip(). Цвет задаётся кортежем RGB (255, 0, 0) или названием вроде "red".
screen.fill((20, 20, 30)) # фон
pygame.draw.rect(screen, (50, 120, 220), (100, 100, 80, 40)) # прямоугольник: x, y, ширина, высота
pygame.draw.circle(screen, "gold", (320, 240), 25) # круг: центр и радиус
pygame.draw.line(screen, "white", (0, 0), (640, 480), 2) # линия толщиной 2
Картинки загружаются из файла и выводятся методом blit():
player_img = pygame.image.load("player.png").convert_alpha() # после set_mode()
screen.blit(player_img, (200, 300)) # левый верхний угол картинки
convert_alpha() переводит картинку в формат экрана с прозрачностью — отрисовка становится заметно быстрее. Вызывать его можно только после создания окна.
Текст рисуется через шрифт: сначала строка превращается в поверхность, затем выводится как картинка.
font = pygame.font.Font(None, 36) # None — встроенный шрифт, 36 — размер
text = font.render("Счёт: 10", True, "white") # True — сглаживание
screen.blit(text, (10, 10))
Встроенный шрифт pygame содержит кириллицу. Для своего шрифта передайте путь к файлу .ttf вместо None.
Прямоугольники Rect и столкновения
Положение и размеры объектов в pygame удобно хранить в pygame.Rect. У него есть координаты (x, y, center, bottom и другие) и готовые проверки пересечений:
player = pygame.Rect(0, 0, 10, 10)
enemy = pygame.Rect(5, 5, 10, 10)
print(player.colliderect(enemy)) # → True: прямоугольники пересекаются
print(player.collidepoint(3, 3)) # → True: точка внутри
У любой картинки можно получить прямоугольник нужного размера: rect = player_img.get_rect(center=(320, 240)). Метод rect.clamp_ip(screen.get_rect()) не даёт объекту выйти за край окна.
Спрайты и группы
Когда объектов становится много, используйте класс pygame.sprite.Sprite. Спрайт хранит картинку image и прямоугольник rect, а группы pygame.sprite.Group обновляют и рисуют все спрайты одной командой:
class Coin(pygame.sprite.Sprite):
def __init__(self, x):
super().__init__()
self.image = pygame.Surface((20, 20), pygame.SRCALPHA) # прозрачный фон
pygame.draw.circle(self.image, (240, 200, 40), (10, 10), 10)
self.rect = self.image.get_rect(midtop=(x, -20))
def update(self, dt):
self.rect.y += round(200 * dt)
coins = pygame.sprite.Group()
coins.add(Coin(100), Coin(300))
coins.update(dt) # вызовет update(dt) у каждого спрайта
coins.draw(screen) # нарисует image каждого спрайта в позиции rect
Столкновение спрайта с группой проверяет pygame.sprite.spritecollide(). С dokill=True задетые спрайты сразу удаляются из всех групп:
caught = pygame.sprite.spritecollide(player, coins, dokill=True)
score += len(caught)
Метод sprite.kill() удаляет спрайт из всех групп — например, когда он улетел за экран. Если классы для вас пока в новинку, сначала прочитайте статью про классы в Python.
Мини-игра «Лови монеты»
Соберём всё вместе. Игрок — синяя платформа внизу экрана, управляется стрелками. Сверху падают монеты, за каждую пойманную начисляется очко. Новые монеты появляются по таймеру через пользовательское событие.
import random
import pygame
WIDTH, HEIGHT = 640, 480
FPS = 60
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((80, 20))
self.image.fill((50, 120, 220))
self.rect = self.image.get_rect(midbottom=(WIDTH // 2, HEIGHT - 10))
self.speed = 400 # пикселей в секунду
def update(self, dt):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.rect.x -= round(self.speed * dt)
if keys[pygame.K_RIGHT]:
self.rect.x += round(self.speed * dt)
self.rect.clamp_ip(pygame.Rect(0, 0, WIDTH, HEIGHT)) # не выходить за экран
class Coin(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((20, 20), pygame.SRCALPHA)
pygame.draw.circle(self.image, (240, 200, 40), (10, 10), 10)
self.rect = self.image.get_rect(midtop=(random.randint(10, WIDTH - 10), -20))
self.speed = random.randint(150, 300)
def update(self, dt):
self.rect.y += round(self.speed * dt)
if self.rect.top > HEIGHT:
self.kill() # упала мимо — удалить из всех групп
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Лови монеты")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 36)
player = Player()
coins = pygame.sprite.Group()
all_sprites = pygame.sprite.Group(player)
SPAWN_COIN = pygame.event.custom_type()
pygame.time.set_timer(SPAWN_COIN, 700) # новая монета каждые 0,7 с
score = 0
running = True
while running:
dt = clock.tick(FPS) / 1000 # секунды с прошлого кадра
# 1. События
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
elif event.type == SPAWN_COIN:
coin = Coin()
coins.add(coin)
all_sprites.add(coin)
# 2. Обновление
all_sprites.update(dt)
caught = pygame.sprite.spritecollide(player, coins, dokill=True)
score += len(caught)
# 3. Отрисовка
screen.fill((20, 20, 30))
all_sprites.draw(screen)
text = font.render(f"Счёт: {score}", True, (255, 255, 255))
screen.blit(text, (10, 10))
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
Сохраните код в файл game.py и запустите: python game.py (в macOS и Linux — python3 game.py). Случайные позиции и скорости монет даёт модуль random.
Идеи для доработки: ограничить игру 60 секундами и показать итоговый счёт, добавить «бомбы», которые отнимают очки, заменить фигуры картинками, добавить звук через pygame.mixer.Sound("coin.wav").play().
Частые ошибки
Окно открывается и сразу закрывается или «не отвечает»
В программе нет игрового цикла или в цикле не вызывается pygame.event.get(). Цикл с обработкой событий обязателен — без него окно не получает сообщений от системы.
Ничего не рисуется
Забыт pygame.display.flip() (или pygame.display.update()) в конце кадра. Ещё одна причина — фон заливается после отрисовки объектов и закрывает их.
Объект оставляет за собой «шлейф»
В начале кадра не вызывается screen.fill(...), и старые изображения остаются на холсте.
pygame.error: video system not initialized
Вызов функций pygame после pygame.quit() или до pygame.init(). Чаще всего — код после цикла, который ещё пытается рисовать.
pygame.error: No video mode has been set
convert() или convert_alpha() вызваны до pygame.display.set_mode() (если не было и pygame.init(), текст ошибки другой — cannot convert without pygame.display initialized). Загружайте картинки после создания окна.
FileNotFoundError при загрузке картинки
Относительный путь считается от текущей папки терминала, а не от папки скрипта. Запускайте игру из её папки или стройте путь от файла: Path(__file__).parent / "player.png".
Ошибка сборки при pip install pygame
Для вашей версии Python нет готовой сборки (см. раздел об установке). Используйте Python 3.13 или пакет pygame-ce.
Частые вопросы
Почему вместо русских букв выводятся прямоугольники?
В выбранном шрифте нет кириллицы — так бывает с декоративными и «пиксельными» шрифтами из интернета. Используйте встроенный шрифт pygame.font.Font(None, 36) (в нём кириллица есть) или файл .ttf с поддержкой кириллицы, положив его рядом с игрой. Системные шрифты подключает pygame.font.SysFont("arial", 24): если такого шрифта нет, pygame подставит встроенный. Список доступных системных шрифтов возвращает pygame.font.get_fonts().
Как сделать задержку, не останавливая игру?
time.sleep() и pygame.time.wait() замораживают весь цикл: окно перестаёт обрабатывать события. Для отложенных действий запоминайте время и сравнивайте его с pygame.time.get_ticks() (миллисекунды с момента pygame.init()):
shield_until = 0
# при подборе бонуса
shield_until = pygame.time.get_ticks() + 3000 # щит на 3 секунды
# в каждом кадре
shield_active = pygame.time.get_ticks() < shield_until
Для повторяющихся событий подходит pygame.time.set_timer(), как в мини-игре выше.
Как проверять столкновения круглых объектов?
colliderect() сравнивает прямоугольники, поэтому круги «сталкиваются» уже углами своих рамок. Для круглых спрайтов задайте атрибут radius и передайте pygame.sprite.collide_circle в spritecollide():
caught = pygame.sprite.spritecollide(
player, coins, dokill=True, collided=pygame.sprite.collide_circle
)
Если атрибута radius нет, pygame вычислит радиус как половину диагонали rect. Для объектов произвольной формы есть pygame.sprite.collide_mask — проверка по непрозрачным пикселям.
Как добавить фоновую музыку?
Длинные треки проигрывает модуль pygame.mixer.music — он читает файл потоком, не загружая его в память целиком:
pygame.mixer.music.load("theme.ogg")
pygame.mixer.music.set_volume(0.5) # от 0.0 до 1.0
pygame.mixer.music.play(-1) # -1 — повторять бесконечно
Короткие звуки (выстрел, монета) загружайте через pygame.mixer.Sound — они могут звучать одновременно с музыкой.
Как называются клавиши Shift, Ctrl и Tab?
Константы клавиш начинаются с K_: pygame.K_LSHIFT и pygame.K_RSHIFT (левый и правый Shift), pygame.K_LCTRL, pygame.K_TAB, pygame.K_RETURN. Чтобы узнать, зажат ли Shift вместе с другой клавишей, проверьте модификаторы события: if event.mod & pygame.KMOD_SHIFT:. Имя любой клавиши по коду возвращает pygame.key.name(event.key) — удобно для отладки.
Итог
Игра на Pygame — это цикл из трёх шагов: обработать события, обновить состояние, нарисовать кадр, а Clock.tick() держит стабильный FPS. Для движения используйте dt, для объектов — спрайты и группы, для столкновений — Rect и spritecollide(). Если игры не ваша цель, но нужен интерфейс с кнопками и полями, посмотрите Tkinter; больше идей для практики — в подборке проектов на Python, а обзор других пакетов — в статье о библиотеках Python. Полный справочник — в документации pygame.
- #Python
- #Pygame
- #Игры
- #Спрайты