Разработка игр на Python
Разработка игр на Python
Основы разработки игр на Python
Python не является основным языком в индустрии разработки игр. Его динамическая типизация, интерпретируемая природа и относительно низкая производительность делают его малопригодным для создания ресурсоёмких проектов — таких как AAA-игры или масштабные многопользовательские онлайн-платформы. Однако Python остаётся мощным инструментом в контексте обучения, прототипирования, скриптования игровой логики и реализации простых 2D-приложений.
Для этих целей существует ряд библиотек, позволяющих организовать графический вывод, обработку ввода, аудиосопровождение и базовую физику. Наиболее известной и долгоживущей из них является Pygame — кроссплатформенная библиотека, построенная на основе SDL (Simple DirectMedia Layer). Она предоставляет минимально необходимый набор средств для создания 2D-игр и используется преимущественно в образовательных целях, а также в инди-проектах небольшого масштаба.
Python не предназначен для создания игровых движков уровня Unreal Engine или Unity. Тем не менее, он может эффективно применяться как средство обучения основам геймдева, анализа алгоритмов поведения персонажей, реализации ИИ в играх, а также как платформа для быстрого прототипирования механик.
Что касается других технологий, то Godot поддерживает язык GDScript, синтаксически близкий к Python, но это не Python. Начиная с версии 4.0, Godot также предлагает официальную поддержку Python через модуль GDExtension (на базе Cython), однако использование Python в Godot — скорее исключение, чем правило. Основная экосистема Godot ориентирована на GDScript, C# и C++. Таким образом, утверждение о том, что Godot "использует Python" — упрощение, не соответствующее действительности в полной мере.
В рамках данной главы мы сосредоточимся на Pygame — наиболее доступной и документированной библиотеке для разработки 2D-игр на чистом Python. Короткие готовые скрипты (змейка, Pong, Breakout и др.) с построчным разбором — в Pygame — мини-игры на Python. Пошаговые крупные проекты (Tetris, Match3, аркады и др.) собраны в Практикуме разработки игр.
Если цель — мобильная игра с сенсорным управлением на том же Python, см. Kivy — мобильные приложения и игры на Python и Практикум Kivy (2048, Pong, Snake).
Если ученик учит Python через Minecraft Java (внешние скрипты mcpi, RaspberryJuice), сначала полезны встроенные команды и datapack, затем Разработка в Minecraft.
Для трёхмерной графики (камера, освещение, 3D-модели) см. отдельную главу Трёхмерная графика и Panda3D — движок Panda3D с примерами карточки, текстуры и куба. Если нужен редактор движка и C# вместо Python — Разработка на Unity и готовые MonoBehaviour в Lab.
Для окон с формами и кнопками без игровой логики — Tkinter — окна и виджеты (теория GUI).
Pygame — это набор модулей Python, предоставляющий доступ к низкоуровневым функциям мультимедиа через привязки к библиотеке SDL. Он позволяет работать с графикой, звуком, вводом с клавиатуры, мыши и геймпадов, а также обеспечивает базовые средства для обработки времени и столкновений.
Библиотека не является игровым движком в строгом смысле слова. У неё отсутствуют встроенные системы анимаций, физики, сцен, шейдеров или редактор ресурсов. Вместо этого Pygame предоставляет низкоуровневые примитивы, которые разработчик должен компоновать самостоятельно, реализуя игровую логику "с нуля".
Тем не менее, именно эта особенность делает Pygame ценным инструментом для обучения: он заставляет разработчика понимать внутренние механизмы работы игрового цикла, обработки событий и управления состоянием объектов.
Консольные игры
Перед графикой и Pygame полезно собрать несколько текстовых игр в терминале. Они тренируют те же навыки — цикл, условия, случайные числа, хранение состояния. Любая такая игра строится по схеме ввод → обновление состояния → вывод.
Минимальный "игровой цикл" без графики:
while True:
cmd = input("команда (стоп): ")
if cmd == "стоп":
break
print("Выполнено:", cmd)
Разбор фрагмента:
while Trueзадаёт бесконечный цикл игры/приложения.input(...)получает действие пользователя, аif ... break— условие завершения цикла.- Это минимальный шаблон game loop: ввод -> проверка условия -> реакция.
Угадай число — классический первый проект с random и while:
import random
secret = random.randint(1, 10)
while True:
guess = int(input("Число от 1 до 10: "))
if guess == secret:
print("Угадал!")
break
print("Ещё раз!")
Камень, ножницы, бумага — сравнение ввода пользователя со случайным выбором:
import random
choices = ["камень", "ножницы", "бумага"]
player = input("Твой ход: ")
bot = random.choice(choices)
print("Компьютер:", bot)
if player == bot:
print("Ничья")
elif (player, bot) in [("камень", "ножницы"), ("ножницы", "бумага"), ("бумага", "камень")]:
print("Ты победил!")
else:
print("Компьютер победил")
Виселица (упрощённо) — строка, множество угаданных букв и счётчик попыток:
Код ITЗагрузка примера кода…
Когда логика в консоли работает, тот же цикл "состояние + ввод + обновление" переносится в Pygame — меняется только способ вывода (окно вместо print). Готовые мини-игры с разбором строк — в Pygame — мини-игры на Python — змейка, Pong, Breakout, тест реакции и др.
Pygame
Pygame представляет собой набор модулей Python, предоставляющий доступ к низкоуровневым функциям мультимедиа через привязки к библиотеке SDL (Simple DirectMedia Layer). Библиотека обеспечивает работу с графикой, звуком, вводом с клавиатуры, мыши и геймпадов. Система включает средства обработки времени и обнаружения столкновений.
Библиотека строится на основе SDL, написанной на языке C. Python-код выступает обёрткой, передающей команды в низкоуровневую среду. Это определяет производительность и кроссплатформенность. Поддерживаются операционные системы Windows, macOS, Linux.
Установка выполняется через менеджер пакетов pip:
pip install pygame
Проверка установки возможна через вывод версии модуля:
import pygame
print(pygame.ver)
Разбор фрагмента:
- Импорт
pygameпроверяет доступность библиотеки в окружении. pygame.verвыводит версию установленного пакета и помогает быстро диагностировать несовместимости.
Любое приложение на Pygame содержит базовый набор вызовов. Отсутствие любого из них приводит к ошибке выполнения или некорректной работе графического интерфейса.
- Импорт модуля:
import pygame. - Инициализация:
pygame.init(). Запускает внутренние подсистемы (видео, аудио, ввод). - Создание окна:
pygame.display.set_mode(). Возвращает объект поверхности экрана. - Игровой цикл: Бесконечный цикл
while, обрабатывающий события и обновляющий кадр. - Завершение:
pygame.quit()и выход из интерпретатора.
Пример минимальной структуры:
Код ITЗагрузка примера кода…
Разбор фрагмента:
pygame.init()инициализирует подсистемы (видео, ввод, звук).- В цикле
pygame.event.get()обрабатываются события окна, включаяQUIT. screen.fill(...)очищает кадр перед отрисовкой, аdisplay.flip()выводит готовый кадр на экран.pygame.quit()освобождает ресурсы SDL при завершении.
Тот же каркас с комментариями к каждой строке и таблицей типичных ошибок — в Lab: мини-игры на Pygame.
Библиотека разделена на логические модули. Каждый модуль отвечает за конкретную задачу.
| Модуль | Назначение | Ключевые объекты |
|---|---|---|
pygame.display | Управление окном и экраном | Surface, set_mode, flip, update |
pygame.draw | Рисование примитивов | circle, rect, line, polygon |
pygame.font | Работа со шрифтами и текстом | Font, render, SysFont |
pygame.event | Обработка событий ввода | get, poll, QUIT, KEYDOWN |
pygame.time | Контроль времени и кадров | Clock, tick, get_ticks |
pygame.image | Загрузка и сохранение изображений | load, save, fromstring |
pygame.mixer | Воспроизведение звука | Sound, music, play, stop |
pygame.sprite | Управление игровыми объектами | Sprite, Group, spritecollide — справочник |
pygame.rect | Прямоугольники и коллизии | Rect, colliderect, move |
pygame.key | Состояние клавиатуры | get_pressed, name |
pygame.mouse | Состояние мыши | get_pos, get_pressed, set_visible |
Вызов pygame.init() активирует все доступные модули. Существует возможность инициализации отдельных подсистем для экономии ресурсов, например pygame.display.init() или pygame.mixer.init(). Функция pygame.get_init() возвращает статус инициализации. Функция pygame.quit() выключает все модули.
Простое управление спрайтом
Чтобы реализовать простейшее управление через W, A, S, D, в Pygame необходимо выполнить следующие шаги:
- Создать класс, наследуемый от
pygame.sprite.Sprite. - Инициализировать изображение (или цветной прямоугольник для прототипа) и задать его позицию (
rect). - Обработать события клавиатуры (
KEYDOWNиKEYUP) для отслеживания нажатия и отпускания клавиш. - В методе обновления (
update) изменять координаты спрайта в зависимости от текущего состояния клавиш. - Добавить проверку границ экрана, чтобы спрайт не уходил за пределы видимой области.
Ниже приведен полный, рабочий пример кода. Сохраните его в файл main.py и запустите из терминала:
pip install pygame
python main.py
Закрыть окно — крестик или Alt+F4. Если используете картинки (pygame.image.load), файлы должны лежать в той же папке, что и скрипт.
Код ITЗагрузка примера кода…
Ключевые моменты реализации:
- Состояние клавиш: Вместо прямого изменения координат внутри обработчика событий используется словарь
keys_pressed. Это позволяет реализовать плавное движение при одновременном нажатии нескольких клавиш (например, диагональ W+A). Если менять координаты только в событииKEYDOWN, движение будет рывками. - Границы экрана: В методе
updateдобавлены проверки (self.rect.top > 0и т.д.), чтобы спрайт не выходил за пределы игрового поля. - Оптимизация: Использование
pygame.sprite.Groupупрощает управление несколькими объектами, если в будущем потребуется добавить врагов или препятствия. - Объективность: Код использует стандартные возможности Pygame без сторонних библиотек, что гарантирует совместимость и предсказуемость поведения.
Если вам потребуются более сложные механики (столкновения, анимация, использование изображений .png вместо цветных квадратов), это можно реализовать путем загрузки изображения через pygame.image.load() и добавления методов обработки коллизий.
В Lab уже разобраны спрайты, Group и spritecollide на примере шутера — вид сверху — стрельба. Змейка и Pong там же: змейка, Pong.
Пошаговое расширение — враг, столкновения, снаряды
Тот же каркас — события → update() → отрисовка — масштабируется по шагам. Каждый новый объект — отдельный класс-спрайт и своя Group.
Шаг 1. Враг. Класс наследует pygame.sprite.Sprite, движение только в update():
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((30, 30))
self.image.fill((0, 255, 0))
self.rect = self.image.get_rect(center=(400, 100))
self.speed_x = 3
self.direction = 1 # 1 — вправо, -1 — влево
def update(self):
self.rect.x += self.speed_x * self.direction
if self.rect.right >= SCREEN_WIDTH or self.rect.left <= 0:
self.direction *= -1
Второй противник — преследование игрока. Тот же каркас спрайта, но в update() враг сравнивает свои координаты с player и сдвигается к нему по осям. Ссылку на игрока передаём в конструктор:
Код ITЗагрузка примера кода…
По диагонали такой враг движется быстрее, чем по одной оси; для равной скорости в любом направлении нормализуйте вектор к игроку (см. раздел "Спрайт противника и движение" ниже).
В main() добавьте enemies = pygame.sprite.Group(), создайте обоих врагов и включите их в all_sprites и enemies:
enemies = pygame.sprite.Group()
patrol = Enemy()
chaser = ChasingEnemy(player)
all_sprites.add(patrol, chaser)
enemies.add(patrol, chaser)
Шаг 2. Столкновение врага с игроком. После all_sprites.update():
hits = pygame.sprite.spritecollide(player, enemies, False)
if hits:
running = False # или урон, отбрасывание, мигание
Третий аргумент dokill: False — враг остаётся; True — спрайт удаляется из всех групп при касании.
Шаг 3. Снаряды и попадания. Отдельная группа bullets. Снаряд двигается в update() и исчезает за краем экрана:
Код ITЗагрузка примера кода…
Выстрел по клику мыши (направление от игрока к курсору):
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
mx, my = event.pos
dx = mx - player.rect.centerx
dy = my - player.rect.centery
length = (dx ** 2 + dy ** 2) ** 0.5
if length > 0:
dx /= length
dy /= length
bullet = Bullet(player.rect.centerx, player.rect.centery, dx, dy)
all_sprites.add(bullet)
bullets.add(bullet)
Попадание по врагам (True уничтожает врага при попадании):
for bullet in bullets:
hits = pygame.sprite.spritecollide(bullet, enemies, True)
if hits:
bullet.kill()
Стрельба по клавише SPACE с фиксированным направлением (например, вверх — dir_x=0, dir_y=-1) — проще, но менее гибко, чем прицеливание мышью.
Кулдаун. Событие MOUSEBUTTONDOWN срабатывает один раз за клик; для ограничения частоты огня храните время последнего выстрела через pygame.time.get_ticks() и сравнивайте с задержкой в миллисекундах.
Итог по группам из пошагового расширения:
all_sprites— все объекты, которые нужно обновлять и рисовать каждый кадрenemies— только враги, для проверки касания с игроком или пулямиbullets— только снаряды, дляgroupcollideсenemies
Дальше — как устроен модуль pygame.sprite целиком. Специализированные классы (DirtySprite, RenderUpdates и др.) собраны в справочнике по pygame.sprite.
Модуль pygame.sprite — архитектура объектов
Спрайт в Pygame — любой видимый игровой объект: игрок, враг, пуля, монетка, кнопка меню. Модуль pygame.sprite помогает хранить такие объекты в группах, рисовать их одной командой и проверять столкновения.
Две опорные сущности:
pygame.sprite.Sprite— шаблон одного объекта (картинка + позиция + логика кадра)pygame.sprite.Group— контейнер со спрайтами; умеет вызыватьupdate()иdraw()сразу для всех членов
Термины, которые встретятся дальше:
Surface(поверхность) — картинка в памяти, набор пикселей; у спрайта лежит вself.imageRect(прямоугольник) — координаты и размер на экране; у спрайта лежит вself.rect; по нему чаще всего считают столкновения- Коллизия (столкновение) — два объекта пересеклись на экране; игра реагирует (урон, сбор предмета, уничтожение пули)
- Хитбокс — зона, по которой проверяют касание; в Pygame это обычно
rect, даже если картинка сложной формы
Перед чтением полезно пройти простое управление спрайтом и пошаговое расширение с врагами и пулями. Там те же идеи на рабочем коде.
Что должен уметь класс-спрайт
pygame.sprite.Sprite сам по себе ничего не рисует. Вы создаёте наследника (class Player(pygame.sprite.Sprite)) и задаёте три вещи:
| Что | Где в коде | Роль |
|---|---|---|
| Картинка | self.image (pygame.Surface) | Что увидит игрок. Файл через pygame.image.load(), для прототипа — цветной Surface |
| Позиция и размер | self.rect (pygame.Rect) | Где объект на экране. Часто self.rect = self.image.get_rect() и сдвиг center / topleft |
| Логика кадра | update(self, ...) | Движение, таймеры, анимация. Group.update() вызовет этот метод у каждого спрайта в группе |
Минимальный каркас:
Код ITЗагрузка примера кода…
В __init__ обязателен вызов super().__init__(). Он регистрирует спрайт во внутренних структурах Pygame; без него add() в группу может не сработать. Про наследование классов — ООП в Python.
Жизненный цикл (add, remove, kill, alive)
Спрайт попадает в игру через группу и исчезает из неё же.
| Метод | Что делает |
|---|---|
sprite.add(group, ...) | Добавляет спрайт в одну или несколько групп |
sprite.remove(group, ...) | Убирает только из перечисленных групп |
sprite.kill() | Убирает из всех групп сразу; стандартный способ убрать пулю или врага |
sprite.alive() | Возвращает True, пока спрайт хотя бы в одной группе |
Параметр dokill у spritecollide и groupcollide делает то же, что ручной kill(), но автоматически для всех объектов из списка столкновений. Пример из пошагового расширения:
spritecollide(player, enemies, False)— при касании враг остаётся (удобно для урона или мигания)spritecollide(bullet, enemies, True)— враг исчезает из групп- после попадания пулю часто вызывают
bullet.kill()отдельно, если у пулиdokill=False
Один спрайт в нескольких группах
Один и тот же объект в памяти можно положить в разные группы. У каждой группы своя задача:
all_sprites— общийupdate()иdraw()в игровом циклеenemies— проверкаspritecollide(player, enemies, ...)bullets— проверкаgroupcollide(bullets, enemies, ...)players— отдельная логика (лидерборд, смена персонажа), если понадобится
Создание врага из практики выше:
patrol = Enemy()
all_sprites.add(patrol)
enemies.add(patrol)
Разбор на готовом шутере — Pygame в Lab, раздел shooter. Крупный проект со спрайтами — Python — Space Invaders.
update и draw у Group
all_sprites = pygame.sprite.Group()
all_sprites.add(player)
# каждый кадр в главном цикле:
all_sprites.update() # у каждого спрайта — свой update()
all_sprites.draw(screen) # screen.blit(image, rect) для всех членов
draw()копируетimageнаscreenв позициюrect(операция blit)- если у спрайта нет
imageилиrect,draw()для него пропустит кадр - порядок рисования в обычном
Groupне фиксирован; кто окажется сверху, может меняться от запуска к запуску - когда нужен стабильный порядок (фон, персонаж, интерфейс), берут
LayeredUpdates— см. ниже и справочник
Функции столкновений
Pygame проверяет касания по-разному: от быстрых прямоугольников до проверки каждого пикселя.
| Функция | Когда применять | Скорость | Точность |
|---|---|---|---|
collide_rect(a, b) | Пули, тайлы, прямоугольные объекты | Высокая | По прямоугольнику rect |
collide_circle(a, b) | Шары, планеты, круглые спрайты | Высокая | По вписанной окружности в rect |
collide_mask(a, b) | Персонажи со сложным силуэтом PNG | Низкая | По непрозрачным пикселям (маски в справочнике) |
spritecollide(sprite, group, dokill) | Игрок и группа врагов, пуля и враги | Зависит от функции проверки | Список всех касаний |
spritecollideany(sprite, group) | Нужен ответ "коснулся ли хоть кто-то" | Та же | Один спрайт или None |
groupcollide(g1, g2, dokill1, dokill2) | Много пуль против многих врагов | Та же | Словарь пар столкновений |
Проверка проигрыша без перебора списка попаданий:
if pygame.sprite.spritecollideany(player, enemies):
running = False
Если объект описан только pygame.Rect (сетка тайлов, невидимая зона двери), используйте rect1.colliderect(rect2) и rect1.collidepoint(x, y) — подробнее в разделе про Rect и в примере столкновений.
Опциональный аргумент collided — своя функция (спрайт_a, спрайт_b) -> bool. По умолчанию Pygame сравнивает rect. Для масок передают collided=pygame.sprite.collide_mask (справочник, §4).
Слои отрисовки (LayeredUpdates)
HUD (heads-up display) — текст и иконки поверх игры: счёт, жизни, пауза. Их нужно рисовать после фона и персонажа. LayeredUpdates сортирует спрайты по числу _layer: меньшее значение — ниже, большее — выше.
all_sprites = pygame.sprite.LayeredUpdates()
class Background(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((800, 600))
self.image.fill((30, 30, 50))
self.rect = self.image.get_rect()
self._layer = 0
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((40, 40))
self.image.fill((255, 80, 0))
self.rect = self.image.get_rect(center=(400, 300))
self._layer = 10
class HudLabel(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
font = pygame.font.SysFont("arial", 24)
self.image = font.render("Score: 0", True, (255, 255, 255))
self.rect = self.image.get_rect(topleft=(10, 10))
self._layer = 100
all_sprites.add(Background(), Player(), HudLabel())
# draw() рисует сначала _layer=0, затем 10, затем 100
Текст на экране через pygame.font разобран в разделе про шрифты. Частичное обновление экрана (RenderUpdates, DirtySprite) — справочник по pygame.sprite.
Окно и поверхности (Surfaces)
Модуль display контролирует графическое окно. Центральным объектом выступает поверхность (Surface), возвращаемая функцией set_mode().
Функция set_mode(size, flags=0, depth=0) принимает кортеж размеров (ширина, высота). Флаги определяют режим отображения.
| Флаг | Описание |
|---|---|
pygame.FULLSCREEN | Разворачивает окно на весь экран |
pygame.RESIZABLE | Позволяет пользователю менять размер окна |
pygame.NOFRAME | Убирает рамку и заголовок окна |
pygame.HIDDEN | Создаёт окно в скрытом режиме |
Центральным понятием в Pygame является поверхность (Surface) — двумерный массив пикселей, представляющий собой область рисования. Каждое изображение, текст или графический элемент в Pygame является экземпляром pygame.Surface.
# Создание новой поверхности размером 100x100 с прозрачностью
surf = pygame.Surface((100, 100), pygame.SRCALPHA)
surf.fill((255, 0, 0, 128)) # Красный цвет с полупрозрачностью
Главное окно игры создаётся с помощью pygame.display.set_mode(), которое возвращает объект Surface, связанный с экраном. Все последующие операции рисования (blitting) выполняются на этой или других поверхностях.
import pygame
screen = pygame.display.set_mode((800, 600))
Поверхности могут быть прозрачными (с альфа-каналом), иметь цветовой ключ (colorkey) для маскирования фона, а также подвергаться трансформациям — масштабированию, повороту, отражению.
Изменения на поверхности не отображаются автоматически. Требуется явный вызов функции обновления.
| Функция | Описание |
|---|---|
pygame.display.flip() | Обновляет весь экран. Подходит для двойной буферизации |
pygame.display.update(rectangle) | Обновляет указанную область экрана. Экономит ресурсы |
Заголовок и иконка тоже могут быть изменены:
pygame.display.set_caption("Название игры")
icon = pygame.image.load("icon.png")
pygame.display.set_icon(icon)
Координатная система
В Pygame начало координат (0, 0) находится в левом верхнем углу экрана. Ось X направлена вправо. Ось Y направлена вниз.
Это отличается от стандартной математической системы, где Y направлен вверх. При расчёте физики необходимо учитывать инверсию оси Y.
Размеры окна задаются в пикселях. Координаты объектов хранятся в целых числах. Дробные координаты возможны внутри логики игры, но отрисовка требует целочисленных значений.
Рисование
Модуль draw содержит функции для рисования геометрических фигур непосредственно на поверхности.
| Функция | Параметры | Описание |
|---|---|---|
rect | surface, color, rect, width=0 | Рисует прямоугольник. width=0 заполняет фигуру |
polygon | surface, color, points, width=0 | Рисует многоугольник по списку точек |
circle | surface, color, center, radius, width=0 | Рисует круг |
ellipse | surface, color, rect, width=0 | Рисует эллипс внутри прямоугольника |
arc | surface, color, rect, start_angle, stop_angle, width=1 | Рисует дугу |
line | surface, color, start_pos, end_pos, width=1 | Рисует линию между двумя точками |
lines | surface, color, closed, points, width=1 | Рисует набор соединённых линий |
aaline | surface, color, start_pos, end_pos, blend=1 | Рисует сглаженную линию |
aalines | surface, color, closed, points, blend=1 | Рисует набор сглаженных линий |
Пример рисования фигуры:
pygame.draw.rect(screen, (0, 255, 0), (50, 50, 100, 100))
pygame.draw.circle(screen, (0, 0, 255), (200, 200), 50)
Класс pygame.Rect хранит координаты и размеры прямоугольной области. Используется для позиционирования и проверки столкновений.
# Из координат и размеров
rect = pygame.Rect(x, y, width, height)
# Из поверхности
rect = surface.get_rect()
# Из кортежа
rect = pygame.Rect((10, 10, 50, 50))
Объект Rect обладает набором атрибутов для доступа к координатам сторон и углов.
| Атрибут | Описание |
|---|---|
x, y | Координаты левого верхнего угла |
width, height | Размеры прямоугольника |
top, left, bottom, right | Координаты сторон |
center, centerx, centery | Координаты центра |
topleft, topright, bottomleft, bottomright | Координаты углов |
midtop, midleft, midbottom, midright | Координаты середин сторон |
size | Кортеж (width, height) |
w, h | Короткие aliases для width и height |
Методы Rect:
| Метод | Описание |
|---|---|
colliderect(other_rect) | Возвращает True при пересечении с другим Rect |
collidepoint(x, y) | Возвращает True, если точка внутри Rect |
collidelist(list) | Проверяет пересечение со списком Rect |
move(x, y) | Возвращает новый Rect, смещённый на x, y |
move_ip(x, y) | Смещает текущий Rect на x, y (in place) |
clamp(other_rect) | Перемещает Rect внутрь другого Rect |
clip(other_rect) | Возвращает пересечение двух Rect |
union(other_rect) | Возвращает объединение двух Rect |
unionall(list) | Возвращает объединение со списком Rect |
fit(other_rect) | Масштабирует и перемещает Rect внутрь другого |
normalize() | Исправляет отрицательную ширину или высоту |
Текст и шрифты (font)
Модуль font позволяет рендерить текст на поверхностях. Текст в Pygame является изображением.
# Системный шрифт
font = pygame.font.SysFont("Arial", 24)
# Загрузка из файла
font = pygame.font.Font("file.ttf", 24)
# Шрифт по умолчанию
font = pygame.font.Font(None, 24)
Метод render(text, antialias, color, background=None) создаёт поверхность с текстом.
| Параметр | Описание |
|---|---|
text | Строка для отображения |
antialias | Boolean. Сглаживание границ букв |
color | Цвет текста (RGB или RGBA) |
background | Цвет фона текста (опционально) |
Пример вывода текста:
text_surface = font.render("Счёт: 10", True, (255, 255, 255))
screen.blit(text_surface, (10, 10))
Доступные свойства Font:
| Свойство | Описание |
|---|---|
get_height() | Высота символов в пикселях |
get_linesize() | Высота строки |
get_ascent() | Подъём шрифта над базовой линией |
get_descent() | Спуск шрифта под базовую линию |
size(text) | Возвращает кортеж (width, height) текста |
metrics(text) | Возвращает метрики для каждого символа |
set_bold(bool) | Устанавливает жирное начертание |
set_italic(bool) | Устанавливает курсив |
set_underline(bool) | Устанавливает подчёркивание |
get_bold(), get_italic(), get_underline() | Возвращают статус стилей |
События и ввод (event)
Система событий обрабатывает действия пользователя и системы. События помещаются в очередь.
for event in pygame.event.get():
# Обработка
Функция pygame.event.get() возвращает список событий за последний кадр. Функция pygame.event.poll() возвращает одно событие или pygame.NOEVENT.
Типы событий:
| Тип | Описание |
|---|---|
pygame.QUIT | Пользователь нажал кнопку закрытия окна |
pygame.KEYDOWN | Клавиша нажата |
pygame.KEYUP | Клавиша отпущена |
pygame.MOUSEMOTION | Движение мыши |
pygame.MOUSEBUTTONDOWN | Нажатие кнопки мыши |
pygame.MOUSEBUTTONUP | Отпускание кнопки мыши |
pygame.JOYAXISMOTION | Движение оси геймпада |
pygame.JOYBUTTONDOWN | Нажатие кнопки геймпада |
Событие KEYDOWN содержит атрибут key (код клавиши) и mod (модификаторы Shift, Ctrl).
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
jump()
if event.key == pygame.K_ESCAPE:
running = False
Постоянное состояние клавиш проверяется через pygame.key.get_pressed(). Возвращает кортеж булевых значений для всех клавиш.
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player.x -= speed
Событие MOUSEBUTTONDOWN содержит атрибут button (1 - левая, 3 - правая, 2 - колесо). Атрибут pos содержит координаты (x, y).
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
shoot(event.pos)
Текущая позиция мыши: pygame.mouse.get_pos().
Состояние кнопок: pygame.mouse.get_pressed().
Для геймпадов же требуется инициализация модуля pygame.joystick.
pygame.joystick.init()
joystick_count = pygame.joystick.get_count()
if joystick_count > 0:
joystick = pygame.joystick.Joystick(0)
joystick.init()
В цикле событий обрабатываются JOYAXISMOTION и JOYBUTTONDOWN. Оси возвращают значения от -1.0 до 1.0.
Время и частота кадров (time)
Модуль time контролирует скорость игры.
Объект pygame.time.Clock() регулирует частоту кадров.
clock = pygame.time.Clock()
# В конце цикла
clock.tick(60) # Ограничивает цикл 60 кадрами в секунду
Разбор фрагмента:
Clock()управляет темпом главного цикла.tick(60)ограничивает частоту до 60 FPS, чтобы игра не перегружала CPU и шла с предсказуемой скоростью.- Возвращаемое значение
tickможно использовать как delta-time для физики.
Метод tick(framerate) задерживает выполнение, чтобы достичь указанной частоты. Возвращает время в миллисекундах с прошлого вызова.
Функция pygame.time.get_ticks() возвращает время в миллисекундах с момента инициализации pygame.init(). Используется для таймеров.
start_time = pygame.time.get_ticks()
# В цикле
current_time = pygame.time.get_ticks()
if current_time - start_time > 1000:
print("Прошла секунда")
start_time = current_time
Функция pygame.time.delay(ms) приостанавливает программу на указанное время. pygame.time.wait(ms) работает аналогично, но блокирует события.
Изображения и звук
Модуль pygame.image загружает файлы в поверхности.
image = pygame.image.load("player.png")
# Конвертация формата для скорости
image = image.convert()
# Конвертация с прозрачностью
image = image.convert_alpha()
Поддерживемые форматы — PNG, JPG, BMP, GIF.
Модуль pygame.mixer управляет аудио.
| Класс/Функция | Описание |
|---|---|
pygame.mixer.Sound(file) | Загружает звуковой эффект в память |
sound.play() | Воспроизводит звук |
sound.stop() | Останавливает звук |
sound.set_volume(val) | Устанавливает громкость (0.0 - 1.0) |
pygame.mixer.music.load(file) | Загружает фоновую музыку (поток) |
pygame.mixer.music.play(loops) | Запускает музыку. loops=-1 для бесконечности |
pygame.mixer.music.stop() | Останавливает музыку |
pygame.mixer.music.set_volume(val) | Громкость музыки |
Пример использования:
jump_sound = pygame.mixer.Sound("jump.wav")
jump_sound.play()
pygame.mixer.music.load("bgm.mp3")
pygame.mixer.music.play(-1)
Разбор фрагмента:
pygame.mixer.Sound(...)подходит для коротких эффектов и быстро воспроизводится по событию.pygame.mixer.music.load(...)используется для фоновой дорожки.play(-1)включает бесконечный цикл музыки, что типично для игрового бэкграунда.
Где брать графику и звук
В Практикуме и Lab первые проекты рисуют объекты примитивами:
pygame.draw.rect,circle,line;- цветные
pygame.Rectбез файлов с диска.
Так проще отлаживать столкновения и игровой цикл, не отвлекаясь на Photoshop. Когда логика работает, подключают ассеты — готовые файлы картинок и звуков.
Куда класть файлы в проекте
- рядом с
main.py(напримерplayer.png); - или в подпапку
assets/images/,assets/sounds/.
Загрузка в коде:
ship = pygame.image.load("assets/images/ship.png").convert_alpha()
shot = pygame.mixer.Sound("assets/sounds/laser.wav")
Подробнее про load, convert и mixer — в разделе Изображения и звук выше на этой странице.
Какие форматы обычно берут
| Назначение | Формат | Примечание |
|---|---|---|
| Спрайт персонажа или врага | PNG | Прозрачный фон — после загрузки вызовите convert_alpha() |
| Фон уровня | PNG или JPG | Для JPG один раз convert() ускоряет отрисовку |
| Короткий эффект (выстрел, взрыв) | WAV или OGG | Класс pygame.mixer.Sound, воспроизведение по событию |
| Фоновая музыка | OGG или MP3 | Модуль pygame.mixer.music, можно зациклить play(-1) |
Каталоги с бесплатными материалами
Перед скачиванием откройте страницу ассета и прочитайте лицензию (можно ли использовать в учебном проекте, нужно ли указать автора).
- OpenGameArt.org — спрайты, тайлсеты, UI, наборы для 2D-аркад.
- Kenney.nl — цельные паки в одном стиле ("Space Shooter", "Platformer"); удобно для прототипа.
- itch.io, раздел game assets — бесплатные и donateware-наборы от инди-авторов.
- Freesound.org — звуковые эффекты; часто лицензии Creative Commons.
- Incompetech — фоновая музыка; в курсовой укажите автора Kevin MacLeod, если того требует лицензия.
Учёт авторства
Добавьте в репозиторий файл CREDITS.txt или блок в README.md:
- имя автора ассета;
- ссылка на страницу;
- тип лицензии (CC0, CC-BY, MIT и т.д.).
Для школьной курсовой обычно достаточно такого списка. В компьютерных играх, правовые аспекты есть общий контекст про контент и лицензии.
Связанные учебные проекты
- Полный клон Space Invaders со счётом, жизнями и тарелкой — практикум Space Invaders.
- Короткая стая врагов в одном файле — Lab, Invaders lite.
- Анимация кадров из одного PNG — sprite sheet ниже в этой главе.
- Звук в Ping Pong практикуме — пример без тяжёлых ассетов.
Цикл игры (Game Loop)
Игровой цикл — фундаментальная структура любой интерактивной программы. В Pygame он реализуется вручную и состоит из трёх этапов:
- Обработка событий (Event Handling)
- Обновление состояния игры (State Update)
- Отрисовка (Rendering) Пример базового цикла:
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Обновление логики
update_game_state()
# Отрисовка
screen.fill((0, 0, 0))
draw_objects(screen)
pygame.display.flip()
Цикл выполняется с максимально возможной частотой, обычно ограниченной с помощью pygame.time.Clock, чтобы обеспечить стабильный FPS (например, 60 кадров в секунду).
События (Events)
Pygame использует систему очереди событий. Все внешние воздействия — нажатия клавиш, движения мыши, закрытие окна — помещаются в очередь и обрабатываются в цикле через pygame.event.get().
События являются объектами с атрибутами type (тип события) и, опционально, дополнительными данными (key, pos, button и т.д.). Это позволяет реализовать реактивную архитектуру, где игра реагирует на пользовательский ввод асинхронно.
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.jump()
Спрайты и группы — краткая сводка
Маршрут по теме в этой главе:
- Простое управление спрайтом — первый
Sprite, клавиши,Group - Враги, столкновения, снаряды —
spritecollide,groupcollide,kill() - Архитектура pygame.sprite — термины, таблица коллизий,
LayeredUpdates
Дополнительно:
- Справочник по pygame.sprite —
DirtySprite,RenderUpdates,GroupSingle, маски, оптимизация - Pygame — мини-игры в Lab — змейка, Pong, шутер с разбором строк
- Анимация спрайта (Sprite Sheet) — кадры из одного PNG
Столкновения без спрайтов
Не каждый объект обязан наследовать Sprite. Иногда достаточно pygame.Rect:
- Тайловая карта — прямоугольник каждой клетки сетки (тайловый фон)
- Зона двери или триггера — невидимый
Rect; при входе игрока меняется уровень - Клик по кнопке меню —
rect.collidepoint(x, y)с координатами мыши
Основные методы Rect:
rect1.colliderect(rect2)— пересекаются ли два прямоугольникаrect1.collidepoint(x, y)— попала ли точка внутрь
Для игровых персонажей и пуль удобнее модуль pygame.sprite — таблица функций. Пиксельная точность по PNG — collide_mask и pygame.mask.from_surface() в справочнике.
Звуки и музыка
Pygame поддерживает воспроизведение аудиофайлов через модули pygame.mixer и pygame.mixer.music.
- pygame.mixer.Sound — для коротких эффектов (выстрел, прыжок).
- pygame.mixer.music — для фоновой музыки (поддерживает потоковое воспроизведение).
jump_sound = pygame.mixer.Sound("jump.wav")
jump_sound.play()
pygame.mixer.music.load("background.mp3")
pygame.mixer.music.play(-1) # зацикливание
Поддерживаются форматы WAV, MP3, OGG. Однако качество и стабильность воспроизведения зависят от платформы и бэкенда SDL.
Пример: реализация "Змейки". Это классический пример, демонстрирующий работу с циклом, вводом, отрисовкой и логикой столкновений.
Основные компоненты:
- Состояние змейки: список координат её сегментов.
- Направление движения: вектор (dx, dy).
- Еда: случайно генерируемая позиция.
- Условия завершения: выход за границы поля или самопересечение.
Полный код:
Код ITЗагрузка примера кода…
Pygame — обучающая платформа, позволяющая освоить ключевые концепции геймдева — игровой цикл, обработку ввода, управление состоянием, коллизии и отрисовку. Он даёт контроль над каждым аспектом, что способствует глубокому пониманию механизмов, лежащих в основе интерактивных приложений.
Для серьёзных проектов рекомендуется использовать специализированные игровые движки — Godot, Unity, Unreal. Однако для обучения, прототипирования или реализации простых 2D-игр Python с Pygame остаётся практичным и доступным выбором.
Практические примеры реализации
Вывод текста на экран
Создание функции для централизованного вывода текста упрощает код.
def draw_text(surface, text, size, x, y):
font = pygame.font.SysFont("serif", size)
text_surface = font.render(text, True, (255, 255, 255))
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
surface.blit(text_surface, text_rect)
Создание спрайта игрока с анимацией
Анимация реализуется через смену изображений по таймеру.
Код ITЗагрузка примера кода…
Спрайт противника и движение
Любое перемещение врага выполняется только в update(), пока в главном цикле вызывается all_sprites.update(). Ниже — типичные паттерны; выберите один под механику игры.
Падение сверху (как в аркадах вроде Space Invaders):
Код ITЗагрузка примера кода…
Отскок по горизонтали — смена direction у границ экрана (см. пошаговый пример выше).
Преследование игрока — сравнение координат rect с целью:
Код ITЗагрузка примера кода…
При одновременном сдвиге по X и Y враг по диагонали движется примерно в 1,4 раза быстрее (сумма двух осей). Для одинаковой скорости в любом направлении нормализуйте вектор:
dx = self.player.rect.centerx - self.rect.centerx
dy = self.player.rect.centery - self.rect.centery
length = (dx ** 2 + dy ** 2) ** 0.5
if length > 0:
self.rect.x += int(dx / length * self.speed)
self.rect.y += int(dy / length * self.speed)
Удобная альтернатива — pygame.math.Vector2 для векторов скорости и нормализации.
Платформа с коллизией
Проверка столкновения игрока с платформой требует проверки направления движения.
Код ITЗагрузка примера кода…
Пуля или снаряд
Снаряд — отдельный спрайт с направлением (vx, vy). Метод kill() удаляет его из всех групп, в которые он входил.
Фиксированное направление (например, вправо по SPACE):
Код ITЗагрузка примера кода…
Выстрел в сторону курсора — вектор от центра игрока к event.pos, нормализация длины, затем создание Bullet с компонентами скорости (см. пошаговый раздел выше).
Попадание по врагам:
hits = pygame.sprite.groupcollide(bullets, enemies, True, True)
# dokill для пуль True, для врагов True — оба удаляются при столкновении
Для спрайтов со сложной формой вместо rect можно использовать pygame.sprite.collide_mask и маски pygame.mask.from_surface() — см. справочник по pygame.sprite.
Бонусы и сбор предметов
Бонусы исчезают при касании игрока.
Код ITЗагрузка примера кода…
Реакция на клик мыши
Спавн объекта в месте клика.
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Левая кнопка
pos = pygame.mouse.get_pos()
effect = Effect(pos[0], pos[1])
all_sprites.add(effect)
Передвижение спрайта с ускорением
Использование векторов скорости и ускорения.
Код ITЗагрузка примера кода…
Вывод сообщений и экран Game Over
Отрисовка поверх игрового процесса.
def draw_game_over(screen):
font = pygame.font.SysFont("Arial", 64)
text = font.render("GAME OVER", True, (255, 0, 0))
rect = text.get_rect(center=(400, 300))
screen.blit(text, rect)
font_small = pygame.font.SysFont("Arial", 24)
text_small = font_small.render("Нажмите R для рестарта", True, (255, 255, 255))
rect_small = text_small.get_rect(center=(400, 350))
screen.blit(text_small, rect_small)
Включение и выключение звуков
Управление микшером через клавиши.
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_m:
if pygame.mixer.music.get_busy():
pygame.mixer.music.pause()
else:
pygame.mixer.music.unpause()
if event.key == pygame.K_s:
volume = pygame.mixer.Sound.get_volume()
if volume > 0:
pygame.mixer.Sound.set_volume(0)
else:
pygame.mixer.Sound.set_volume(1)
Полный пример структуры игры
Код ниже объединяет описанные компоненты в единую систему.
Код ITЗагрузка примера кода…
Play
Библиотека play — сторонняя библиотека, разработанная для упрощения создания 2D-игр и анимаций, особенно в образовательной среде. Она строится на основе Pygame Zero, но предлагает ещё более минималистичный API, ориентированный на начинающих. Установка проста:
pip install play
play следует императивной модели, в которой программа описывает поведение объектов в виде функций обратного вызова (update, draw). Главный цикл управления выполняется автоматически.
Центральным элементом являются спрайты — двухмерные объекты, обладающие позицией, размером, изображением и свойствами анимации. Спрайт создаётся как экземпляр класса play.new_image() или play.new_box():
ball = play.new_circle(color="red", x=0, y=0, radius=25)
Каждый спрайт имеет атрибуты:
- x, y — координаты центра.
- angle — угол поворота.
- size — масштаб относительно исходного изображения.
- image — если используется текстура.
- visible — видимость объекта.
Движение реализуется путём изменения атрибутов спрайтов в функции update(), которая вызывается каждый кадр (обычно 60 раз в секунду):
@play.repeat_forever
def do():
ball.x += 1
Реакция на события оформляется декораторами — без ручного разбора очереди, как в Pygame:
Код ITЗагрузка примера кода…
Также доступны встроенные методы, такие как move(), point_towards(), rotate().
Обнаружение столкновений (коллизий) выполняется с помощью метода is_touching(other_sprite):
if ball.is_touching(paddle):
ball.color = "blue"
Этот метод проверяет пересечение границ спрайтов (bounding boxes), что достаточно для большинства простых случаев, но не учитывает прозрачные области изображений.
play предоставляет простой доступ к состоянию клавиш:
if play.key_is_pressed("w"):
paddle.y += 5
Поддерживаются буквенные, цифровые и специальные клавиши ("space", "up", "left" и т. д.). Нет необходимости вручную управлять event loop — библиотека делает это автоматически.
Рассмотрим минимальную реализацию игры, где игрок должен перемещать платформу, чтобы поймать падающий шарик.
Код ITЗагрузка примера кода…
Этот пример демонстрирует типичную структуру приложения в play — объявление объектов, реакция на события в цикле repeat_forever, использование глобального состояния.
Распространённые механики в Pygame
Обработка столкновений (Collision Detection)
Функции pygame.sprite для спрайтов и сравнение способов проверки — в разделе про столкновения спрайтов. Ниже — отдельный пример на чистых pygame.Rect и кругах (полезно для платформ и физики без классов-наследников).
Код ITЗагрузка примера кода…
Система частиц (Particle System)
Частицы используются для эффектов взрыва, следов от шагов, магии или дождя. Это множество маленьких объектов, которые живут короткое время, уменьшаются или меняют прозрачность.
Код ITЗагрузка примера кода…
Тайловый фон (Tilemap)
Для создания уровня без рисования каждого пикселя вручную используется сетка тайлов. Каждый элемент сетки — это картинка (тайл), которая повторяется.
Код ITЗагрузка примера кода…
Камера (Camera Follow)
Вместо того чтобы двигать игрока по бесконечному миру, мы двигаем мир (фон, врагов) в противоположную сторону от игрока. Это позволяет создавать уровни больше экрана.
Код ITЗагрузка примера кода…
Простой инвентарь (Grid UI)
Создание сетки для отображения предметов. Это база для RPG или стратегий. Мы используем Rect для определения слотов и проверку кликов мыши.
Код ITЗагрузка примера кода…
Анимация спрайта (Sprite Sheet)
Использование одного изображения (спрайт-листа), где содержатся все кадры анимации, и переключение видимых частей.
Примечание: Для примера мы генерируем картинку программно, но в реальном проекте вы загружаете файл .png.
Код ITЗагрузка примера кода…
Пауза и Меню (Game State Management)
Управление состоянием игры позволяет переключаться между "Игрой", "Паузой" и "Меню". Это реализуется через флаг состояния (game_state).
Код ITЗагрузка примера кода…
Система здоровья с визуальным эффектом (Hit Flash)
Когда персонаж получает урон, он должен мигнуть красным или белым цветом. Это делается через таймер и изменение цвета поверхности спрайта.
Код ITЗагрузка примера кода…
Сохранение и загрузка данных (JSON)
Для сохранения прогресса (позиция, здоровье, инвентарь) используется модуль json. Это стандартный способ хранения структурированных данных в текстовом виде.
Код ITЗагрузка примера кода…
Практика и справочник
- Pygame — мини-игры на Python — змейка, Pong, шутер, Breakout с построчным разбором
- Практикум разработки игр — Tetris, Match3, Space Invaders
- Справочник по pygame.sprite — полный API групп, коллизий и оптимизации отрисовки
- Игровой цикл и события · Поверхности и Rect
- Трёхмерная графика и Panda3D · Kivy — мобильные игры
В подборках
Статья входит в тематические подборки и блок "С чего начать?" на главной. Соседние шаги того же маршрута:
Разработка видеоигр — Практикум — о разделе, Веб-игры на HTML5 и Canvas, Разработка игр — о разделе, Разработка игр с использованием C++, Игровая индустрия — о разделе, Игроведение — о разделе.