Чтобы вывести текст в PyGame, нужны три строки: создать шрифт font = pygame.font.SysFont("arial", 36), превратить строку в картинку surface = font.render("Счёт: 10", True, (255, 255, 255)) и нарисовать её на экране screen.blit(surface, (20, 20)). Кнопка делается из pygame.Rect: прямоугольник рисуется через draw.rect, а нажатие ловится в событии MOUSEBUTTONDOWN проверкой rect.collidepoint(event.pos).
Ниже разбираю шрифты, позиционирование, клики, клавиши и собираю всё в меню со счётом. Код снят на pygame 2.6.1, окно и игровой цикл беру из инструкции про первое окно в PyGame; если нужен контекст целой игры, есть разбор, как сделать игру на PyGame с нуля.
SkillboxПрофессия «Python-разработчик»157 335 ₽10 месяцев
НетологияFullstack-разработчик на Python137 000 ₽21 месяц
GeekBrainsДО Профессия Python-разработчик149 001 ₽10 месяцев
SkillFactoryПрофессия Python-разработчик154 044 ₽10 месяцев
Академия СинергияPython-разработчик + ИИ77 900 ₽6 месяцев
ЭдюсонPython-разработчик119 892 ₽9 месяцев
Как вывести текст на экран: pygame font и render

В PyGame нельзя «напечатать» строку прямо на экране. Модуль pygame.font сначала рисует текст на отдельной поверхности (Surface), и уже её вы кладёте на экран как любую картинку. Отсюда три шага, которые повторяются в каждой игре.
- Инициализировать библиотеку:
pygame.init()запускает и модуль шрифтов. Без этогоSysFontпадает с ошибкойpygame.error: font not initialized. - Создать объект шрифта один раз, до игрового цикла:
font = pygame.font.SysFont("arial", 36). - В цикле вызвать
font.render()и нарисовать результат черезscreen.blit().
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
font = pygame.font.SysFont("arial", 36)
text = font.render("Счёт: 10", True, (255, 255, 255))
print(text.get_size()) # (140, 41) — размер поверхности с текстом
screen.blit(text, (20, 20))
pygame.display.flip()
У render() четыре параметра, и путают обычно второй.
| Параметр | Что делает | Пример |
|---|---|---|
text |
Строка. Только одна строка: символ \n не переносит текст, а рисуется пустым квадратом |
"Пауза", f"Счёт: {score}" |
antialias |
Сглаживание краёв. True для обычного текста, False для пиксельного стиля |
True |
color |
Цвет текста: кортеж RGB или имя цвета латиницей | (255, 255, 255), "white" |
background |
Цвет подложки. Без него фон прозрачный | (0, 0, 0) |
Результат render() — обычная поверхность, поэтому к тексту применимо всё, что умеет Surface: get_rect() для позиционирования, pygame.transform.rotate() для поворота, set_alpha() для прозрачности. Официальное описание модуля — в документации pygame.font.
SysFont, Font и файл ttf: какой шрифт выбрать
Шрифт в PyGame создаётся тремя способами, и они по-разному ведут себя на чужом компьютере. Это важно, если игру будут запускать не только вы.
| Способ | Код | Когда подходит |
|---|---|---|
| Системный шрифт | pygame.font.SysFont("arial", 36, bold=True) |
Быстрые прототипы. На машине без Arial PyGame молча подставит шрифт по умолчанию |
| Встроенный шрифт | pygame.font.Font(None, 48) |
Всегда работает: None означает файл freesansbold.ttf, который идёт в комплекте с библиотекой. Кириллицу поддерживает |
| Свой файл | pygame.font.Font("fonts/PressStart2P.ttf", 24) |
Готовая игра: шрифт лежит рядом с кодом и выглядит одинаково у всех |
Проверить, какие шрифты видит PyGame на вашей системе, можно двумя функциями. pygame.font.get_fonts() возвращает список имён в нижнем регистре без пробелов (у меня на macOS их 555, среди них arialunicode, menlo, helvetica), а pygame.font.match_font("arial") отдаёт путь к файлу или None, если шрифта нет.
print(pygame.font.get_default_font()) # freesansbold.ttf
print(pygame.font.match_font("arial")) # //selcdn.checkroi.ru/System/Library/Fonts/Supplemental/Arial.ttf
print(pygame.font.match_font("nosuch")) # None
Начертание меняется двумя путями. У SysFont есть аргументы bold и italic, а у любого объекта шрифта — методы set_bold(), set_italic() и set_underline(). Если библиотека у вас ещё не установлена, начните с короткой инструкции, как установить PyGame через pip.
Позиция текста, счёт и несколько строк
Координаты в blit() задают левый верхний угол поверхности. Чтобы поставить надпись по центру экрана или прижать к нижнему краю, удобнее взять прямоугольник текста и сдвинуть его через именованные атрибуты. Как устроен Rect и его атрибуты вроде center и midbottom, я подробно разбирал в статье про Rect и столкновения в PyGame.
title = font.render("Моя игра", True, (255, 255, 255))
title_rect = title.get_rect(center=(320, 120)) # центр надписи в точке (320, 120)
screen.blit(title, title_rect)
hint = font.render("Пробел: +1, Esc: в меню", True, (160, 160, 160))
screen.blit(hint, hint.get_rect(midbottom=(320, 460))) # по центру у нижнего края
Счёт, который меняется, рендерится заново в каждом кадре: font.render(f"Счёт: {score}", True, WHITE). Сам вызов render() дешёвый, дорого только создание шрифта, поэтому объект font держите вне цикла, а строку обновляйте внутри.
Несколько строк выводятся циклом: разбиваете текст по \n, рендерите каждую строку отдельно и сдвигаете y на высоту строки. Высоту берите из font.get_linesize(), а не подбирайте вручную.
lines = "Первая строка\nВторая строка\nТретья".split("\n")
y = 20
for line in lines:
surface = font.render(line, True, (255, 255, 255))
screen.blit(surface, (20, y))
y += font.get_linesize() # для arial 28 это 33 пикселя
Как сделать кнопку в PyGame: Rect, collidepoint и MOUSEBUTTONDOWN

Готового виджета «кнопка» в PyGame нет, и это нормально: кнопка — это прямоугольник, надпись на нём и проверка, попал ли клик внутрь. Всё это уже есть в библиотеке, остаётся собрать в класс. Ниже класс, который умеет подсвечиваться при наведении и вызывать функцию по клику.
class Button:
def __init__(self, text, x, y, w, h, on_click):
self.rect = pygame.Rect(x, y, w, h)
self.text = text
self.on_click = on_click
self.hover = False
def handle_event(self, event):
if event.type == pygame.MOUSEMOTION:
self.hover = self.rect.collidepoint(event.pos)
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
if self.rect.collidepoint(event.pos):
self.on_click()
def draw(self, surface):
color = (90, 150, 210) if self.hover else (60, 100, 150)
pygame.draw.rect(surface, color, self.rect, border_radius=8)
label = ui_font.render(self.text, True, (255, 255, 255))
surface.blit(label, label.get_rect(center=self.rect.center))
Три вещи, на которых держится этот класс.
- Событие, а не опрос. Клик ловится через
pygame.MOUSEBUTTONDOWNиз очереди событий. Если вместо этого проверятьpygame.mouse.get_pressed()в цикле, одно нажатие сработает на каждом кадре, пока кнопка мыши зажата, и обработчик вызовется десятки раз. event.buttonговорит, какая кнопка нажата:1левая,2колесо как кнопка,3правая. Прокрутка колеса в pygame 2 приходит отдельным событиемpygame.MOUSEWHEELс полемevent.y(-1вниз,1вверх).collidepoint(event.pos)возвращаетTrue, если точка внутри прямоугольника. Тот же метод отвечает за подсветку при наведении черезMOUSEMOTION.
Текущее положение курсора вне событий даёт pygame.mouse.get_pos(), а смещение с прошлого вызова — pygame.mouse.get_rel(). Полный список полей событий мыши и клавиатуры описан в справочнике по pygame.event.
Клавиши в PyGame: KEYDOWN, get_pressed и ввод текста
Клавиатуру в PyGame читают двумя способами, и у каждого своя задача. Событие KEYDOWN срабатывает один раз в момент нажатия, поэтому оно подходит для прыжка, паузы, выстрела и выхода по Esc. Функция pygame.key.get_pressed() возвращает состояние всех клавиш прямо сейчас и нужна для движения: пока стрелка зажата, персонаж едет.
| Задача | Как читать | Код |
|---|---|---|
| Прыжок, пауза, выход | Событие KEYDOWN |
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: |
| Движение, пока клавиша зажата | key.get_pressed() каждый кадр |
keys = pygame.key.get_pressed(); if keys[pygame.K_LEFT]: player.x -= 5 |
| Ввод имени, чата | event.unicode из KEYDOWN |
name += event.unicode |
| Отпускание клавиши | Событие KEYUP |
if event.type == pygame.KEYUP: |
Коды клавиш лежат в константах pygame.K_*: K_SPACE, K_ESCAPE, K_RETURN, K_LEFT, K_a. Имя клавиши по коду отдаёт pygame.key.name(event.key). Для текстового поля используйте не event.key, а event.unicode: там уже лежит символ с учётом раскладки и Shift, включая кириллицу.
name = ""
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_BACKSPACE:
name = name[:-1]
elif event.key == pygame.K_RETURN:
print("Введено:", name)
elif event.unicode.isprintable():
name += event.unicode
Проверка isprintable() отсекает служебные символы: у Esc и стрелок unicode пустой или непечатный, и без неё в строку попадёт мусор. Подробнее о том, как игровой цикл раз за разом опрашивает события, я писал в разборе цикла while в Python. Справочник по константам клавиш — в документации pygame.key. Если условия, классы и словари в этом коде пока читаются с трудом, базу быстрее добрать на курсах по Python, а к PyGame вернуться через неделю.
Меню, кнопки и счёт в одной программе

Ниже полный код: стартовое меню с двумя кнопками, игровое состояние с квадратом, который двигается стрелками, счётчик по пробелу и возврат в меню по Esc. Переключение экранов сделано через переменную state, а не через два вложенных цикла: так проще добавлять экраны паузы и проигрыша, а потом и экран «Игра окончена» с рестартом, как в разборе игры на PyGame от пустого окна до exe.
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption("Меню и счёт")
clock = pygame.time.Clock()
title_font = pygame.font.SysFont("arial", 48, bold=True)
ui_font = pygame.font.SysFont("arial", 28)
WHITE = (255, 255, 255)
BG = (30, 30, 40)
class Button:
def __init__(self, text, x, y, w, h, on_click):
self.rect = pygame.Rect(x, y, w, h)
self.text = text
self.on_click = on_click
self.hover = False
def handle_event(self, event):
if event.type == pygame.MOUSEMOTION:
self.hover = self.rect.collidepoint(event.pos)
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
if self.rect.collidepoint(event.pos):
self.on_click()
def draw(self, surface):
color = (90, 150, 210) if self.hover else (60, 100, 150)
pygame.draw.rect(surface, color, self.rect, border_radius=8)
label = ui_font.render(self.text, True, WHITE)
surface.blit(label, label.get_rect(center=self.rect.center))
state = "menu"
score = 0
player = pygame.Rect(300, 220, 40, 40)
running = True
def start_game():
global state, score
state = "game"
score = 0
def quit_game():
global running
running = False
buttons = [
Button("Играть", 220, 200, 200, 50, start_game),
Button("Выход", 220, 270, 200, 50, quit_game),
]
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif state == "menu":
for button in buttons:
button.handle_event(event)
elif state == "game" and event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
score += 1
elif event.key == pygame.K_ESCAPE:
state = "menu"
if state == "game":
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player.x -= 5
if keys[pygame.K_RIGHT]:
player.x += 5
player.clamp_ip(screen.get_rect())
screen.fill(BG)
if state == "menu":
title = title_font.render("Моя игра", True, WHITE)
screen.blit(title, title.get_rect(center=(320, 120)))
for button in buttons:
button.draw(screen)
else:
pygame.draw.rect(screen, (240, 200, 60), player)
score_text = ui_font.render(f"Счёт: {score}", True, WHITE)
screen.blit(score_text, (20, 20))
hint = ui_font.render("Пробел: +1, Esc: в меню", True, (160, 160, 160))
screen.blit(hint, hint.get_rect(midbottom=(320, 460)))
pygame.display.flip()
clock.tick(60)
pygame.quit()
Что происходит при запуске, по шагам.
- Открывается меню: заголовок по центру и две кнопки. При наведении кнопка светлеет.
- Клик по «Играть» вызывает
start_game(): состояние меняется наgame, счёт обнуляется. - Стрелки двигают квадрат,
clamp_ipне даёт ему уехать за экран, пробел прибавляет очко, счёт обновляется в левом верхнем углу. - Esc возвращает в меню, «Выход» завершает цикл и закрывает окно.
Я прогнал этот код с заскриптованными событиями: наведение включает подсветку, клик переводит в игру, два пробела дают счёт 2, Esc возвращает в меню, клик по «Выход» останавливает цикл. Дальше сюда естественно ложатся спрайты и столкновения из статьи про Rect и спрайты, а если хочется понять, как такие механики устроены в больших проектах, посмотрите обзор, как программировать игры.
Частые ошибки с текстом и кнопками
Эти пять проблем я видел в чужом коде чаще всего, и все они не выглядят как ошибка: программа запускается, а на экране не то.
- Текст не появляется вовсе. Обычно
blit()стоит доscreen.fill(), и заливка фона закрашивает надпись в том же кадре. Порядок в цикле: сначалаfill, потом всё остальное, в концеflip(). - Игра тормозит, когда на экране появился счёт. Значит,
SysFont()вызывается внутри цикла. У меня создание шрифта плюс рендер занимает 0,69 мс, а один рендер готовым шрифтом 0,005 мс: разница в сто с лишним раз. Шрифт создаётся один раз доwhile. - Вместо переноса строки квадрат.
render()рисует одну строку, символ\nон не понимает. Разбивайте текст черезsplit("\n")и рендерите построчно, как в разделе выше. - Одно нажатие срабатывает много раз. Клик или клавиша проверяются через
get_pressed(), а не через событие. Для разовых действий (клик по кнопке, прыжок, пауза) используйтеMOUSEBUTTONDOWNиKEYDOWN. - Кириллица рисуется квадратами. Выбранный системный шрифт не содержит русских глифов. Встроенный
Font(None, размер)кириллицу знает; для своего ttf проверьте, что в нём есть нужные символы, черезfont.metrics("П"): для отсутствующего глифа вернётсяNone.
Ещё одна ловушка с цветом:
render("x", True, "белый")падает сUnicodeEncodeError, потому что имена цветов PyGame понимает только латиницей. Пишите"white"или кортеж(255, 255, 255).
Где освоить Python и разработку игр системно
Текст, кнопки и обработка клавиш закрывают интерфейс маленькой игры, а дальше начинаются уровни, сохранения, звук и сборка в exe. Если PyGame для вас первый шаг к профессии, посмотрите, чем занимается разработчик игр и какие движки просят в вакансиях. Системно язык и логику игр дают курсы по Python и курсы по разработке игр: там PyGame проходят как учебный движок, а потом переводят на Unity или Godot.
| Курс | Школа | Стоимость со скидкой | В рассрочку | Длительность | Обзор курса от Checkroi |
|---|---|---|---|---|---|
| Профессия «Python-разработчик» Перейти на сайт курса | 157 335 ₽ | 5987 ₽/мес. | 10 месяцев | Обзор курса | |
| Fullstack-разработчик на Python Перейти на сайт курса | 137 000 ₽ | 7125 ₽/мес. | 21 месяц | Обзор курса | |
| ДО Профессия Python-разработчик Перейти на сайт курса | 149 001 ₽ | 3167 ₽/мес. | 10 месяцев | Обзор курса | |
| Автоматизированное тестирование на Python Перейти на сайт курса | 122 226 ₽ | 4108 ₽/мес. | 9 месяцев | Обзор курса | |
| Профессия Python-разработчик Перейти на сайт курса | 154 044 ₽ | 4279 ₽/мес. | 10 месяцев | Обзор курса |
Больше программ — в полном каталоге курсов по Python




