Создание классических аркадных игр — один из лучших способов применить теоретические знания Python на практике. В этом руководстве мы с нуля разработаем игру «Змейка», используя библиотеку Pygame — стандартный инструмент для 2D-разработки в экосистеме Python.
Статья рассчитана на читателей, уже знакомых с базовым синтаксисом Python, и ставит целью дать четкий, пошаговый алгоритм для создания законченного проекта. По завершении вы не только получите готовую игру, но и освоите ключевые принципы, которые сможете применить при разработке собственных, более сложных приложений.
Раздел 1: Подготовка и настройка проекта
Прежде чем написать первую строку игрового кода, необходимо создать чистое и изолированное рабочее окружение. Это стандартная практика в Python-разработке, которая предотвращает конфликты между зависимостями разных проектов. Мы также определим базовые параметры нашего будущего приложения: размер окна, цвета и скорость обновления.
Шаг 1: Создание виртуального окружения
Сначала создадим папку для нашего проекта и перейдем в нее через терминал или командную строку.
mkdir snake_project cd snake_project
Теперь внутри этой папки создадим виртуальное окружение с помощью встроенного модуля venv. Мы назовем его venv.
python -m venv venv
Эта команда создаст директорию venv, в которой будут храниться интерпретатор Python и все библиотеки, относящиеся только к этому проекту. Чтобы начать им пользоваться, окружение нужно активировать.
Для Windows:
venv\Scripts\activateДля macOS и Linux:
source venv/bin/activate
После успешной активации вы увидите (venv) в начале командной строки. Это означает, что вы работаете в изолированной среде.

Шаг 2: Установка Pygame
Теперь, когда окружение активно, можно безопасно установить Pygame. Библиотека будет установлена только для нашего проекта, не затрагивая глобальную систему.
pip install pygame
Шаг 3: Создание игрового окна и базовых констант
Внутри папки проекта (snake_project) создайте файл snake_game.py. Откройте его в вашем редакторе кода и добавьте стартовый шаблон. Он будет содержать импорты, инициализацию Pygame, определение основных констант и создание игрового окна.
import pygame # 1. Инициализация Pygame pygame.init() # 2. Определение констант # Размеры окна SCREEN_WIDTH = 640 SCREEN_HEIGHT = 480 # Размер одного блока змейки и еды BLOCK_SIZE = 20 # Цвета (RGB) WHITE = (255, 255, 255) BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 128, 0) # 3. Создание игрового окна screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption('Змейка на Pygame') # 4. Настройка для контроля FPS clock = pygame.time.Clock()
Давайте разберем, что делает этот код:
pygame.init(): Запускает все необходимые модули Pygame. Эту функцию нужно вызывать в самом начале.Константы: Мы выносим в переменные с заглавными буквами значения, которые не будут меняться в ходе игры. Это улучшает читаемость кода и упрощает его изменение в будущем. Например, если вы захотите увеличить игровое поле, достаточно будет поменять значения
SCREEN_WIDTHиSCREEN_HEIGHTв одном месте.pygame.display.set_mode(): Эта функция создает главное окно (поверхность), на которой будет происходить отрисовка. Мы передаем ей кортеж с шириной и высотой.pygame.time.Clock(): Создает специальный объект, который поможет нам управлять скоростью игры, ограничивая количество кадров в секунду (FPS). Это гарантирует, что игра будет работать с одинаковой скоростью на компьютерах разной мощности.
На этом этапе у нас есть готовая основа для проекта: настроенное окружение и код, который создает пустое черное окно с заголовком "Змейка на Pygame".
Раздел 2: Создание игровых объектов (Змейка и Еда)
Теперь, когда у нас есть «сцена» в виде игрового окна, пора добавить на нее «актеров»: змейку, которой будет управлять игрок, и еду, которую она будет собирать. В коде эти объекты будут представлены в виде простых структур данных, хранящих их координаты и состояние.
Этот код следует добавить в ваш файл snake_game.py сразу после блока с константами и созданием окна.
Шаг 1: Представление змейки
Змейка — это не единый объект, а последовательность связанных сегментов. Наиболее удобный способ представить ее в коде — использовать список, где каждый элемент является списком из двух чисел: [x, y] координат сегмента.
Мы определим три переменные для управления змейкой:
snake_pos: Текущие координаты головы змейки. Мы будем обновлять именно их для симуляции движения.snake_body: Список всех сегментов змейки. Первый элемент (snake_body[0]) всегда будет совпадать сsnake_pos.snake_direction: Строковая переменная для хранения текущего направления движения.
# Начальное положение змейки # Голова змейки появляется в определенной позиции snake_pos = [100, 60] # Тело змейки: список списков с координатами каждого сегмента # Изначально состоит из 3-х сегментов snake_body = [[100, 60], [80, 60], [60, 60]] # Начальное направление движения змейки snake_direction = 'RIGHT'
Мы разместили змейку горизонтально, с головой в точке (100, 60). Обратите внимание, что все координаты кратны BLOCK_SIZE (20), что обеспечивает выравнивание объектов по сетке.
Шаг 2: Создание еды
Еда — это одиночный блок, который должен появляться в случайном месте на игровом поле. Как и сегменты змейки, еда будет представлена списком с [x, y] координатами.
Для генерации случайных координат нам понадобится модуль random, который мы импортировали в самом начале.
# Позиция еды # Генерируется случайным образом в пределах игрового поля food_pos = [random.randrange(0, SCREEN_WIDTH // BLOCK_SIZE) * BLOCK_SIZE, random.randrange(0, SCREEN_HEIGHT // BLOCK_SIZE) * BLOCK_SIZE]
Давайте разберем эту строку:
SCREEN_WIDTH // BLOCK_SIZE: Эта операция вычисляет, сколько блоков помещается по ширине экрана. Например,640 // 20 = 32.random.randrange(0, ...): Генерирует случайное целое число — номер ячейки в нашей сетке (например, от 0 до 31).... * BLOCK_SIZE: Умножает случайный номер ячейки на размер блока, чтобы преобразовать его обратно в пиксельные координаты. Например, если сгенерировалось число 5, координата будет5 * 20 = 100.
Этот подход гарантирует, что еда всегда будет появляться ровно в ячейке сетки, а не между ними, что критически важно для механики игры.
На данном этапе мы определили начальное состояние нашей игры. У нас есть все данные, чтобы отрисовать первый кадр: змейку и еду в их стартовых позициях. В следующем разделе мы создадим главный игровой цикл, который заставит эти объекты двигаться и взаимодействовать.
Раздел 3: Сердце игры — главный игровой цикл
Сердце любой игры — это главный игровой цикл (Game Loop). Это непрерывно выполняющийся блок кода, который отвечает за три ключевые задачи на каждом кадре:
Обработка ввода (Input): Проверка действий игрока (нажатия клавиш, движения мыши).
Обновление состояния (Update): Изменение позиций и состояний игровых объектов на основе логики и ввода.
Отрисовка (Render): Перерисовка экрана для отображения нового состояния игры.
Весь последующий код необходимо поместить в конец файла snake_game.py.
Основная структура игрового цикла
Мы создадим флаг game_over, который будет управлять работой цикла. Пока он имеет значение False, игра продолжается.
game_over = False while not game_over: # 1. Обработка событий for event in pygame.event.get(): if event.type == pygame.QUIT: game_over = True elif event.type == pygame.KEYDOWN: if event.key == pygame.K_UP and snake_direction != 'DOWN': snake_direction = 'UP' elif event.key == pygame.K_DOWN and snake_direction != 'UP': snake_direction = 'DOWN' elif event.key == pygame.K_LEFT and snake_direction != 'RIGHT': snake_direction = 'LEFT' elif event.key == pygame.K_RIGHT and snake_direction != 'LEFT': snake_direction = 'RIGHT' # 2. Обновление состояния (пока здесь будет только отрисовка) # Логику движения и столкновений добавим в следующем разделе. # 3. Отрисовка # Заливаем фон черным цветом screen.fill(BLACK) # Рисуем змейку for pos in snake_body: pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], BLOCK_SIZE, BLOCK_SIZE)) # Рисуем еду pygame.draw.rect(screen, RED, pygame.Rect(food_pos[0], food_pos[1], BLOCK_SIZE, BLOCK_SIZE)) # Обновляем экран, чтобы отобразить нарисованное pygame.display.flip() # Устанавливаем FPS (кадры в секунду) clock.tick(15) # Корректное завершение работы pygame.quit() quit()
Подробный разбор цикла
A. Обработка событий
pygame.event.get(): Эта функция получает список всех событий, произошедших с момента ее последнего вызова (нажатия клавиш, клики мыши и т.д.). Мы перебираем этот список в цикле.if event.type == pygame.QUIT: Это событие возникает, когда пользователь нажимает на крестик для закрытия окна. Мы устанавливаемgame_over = True, чтобы выйти из главного цикла.if event.type == pygame.KEYDOWN: Событие означает, что была нажата какая-то клавиша.event.key: Атрибут, который содержит код нажатой клавиши (например,pygame.K_UPдля стрелки вверх).Мы проверяем, какая стрелка была нажата, и обновляем переменную
snake_direction.Важное условие
and snake_direction != '...'не позволяет змейке мгновенно развернуться на 180 градусов (например, с 'UP' на 'DOWN'), что в классической «Змейке» привело бы к мгновенному столкновению с собственным телом.
B. Отрисовка (Рендеринг)
Процесс отрисовки кадра всегда следует одному и тому же порядку:
screen.fill(BLACK): Сначала мы полностью заливаем экран одним цветом (в нашем случае черным). Это необходимо, чтобы стереть изображение с предыдущего кадра.pygame.draw.rect(): Мы используем эту функцию для рисования прямоугольников. Мы проходим в цикле по всем сегментамsnake_bodyи рисуем зеленый квадрат для каждого. Затем рисуем один красный квадрат для еды.pygame.display.flip(): Эта команда делает видимым все, что мы нарисовали на экране. Без нее пользователь ничего бы не увидел.
C. Контроль частоты кадров (FPS)
clock.tick(15): Здесь мы используем созданный ранее объектclock. Эта строка говорит Pygame, что наш цикл должен выполняться не чаще 15 раз в секунду. Это задает скорость игры и обеспечивает ее стабильность на компьютерах разной производительности.
D. Завершение работы
Когда цикл while завершается (когда game_over становится True), выполняются две последние команды:
pygame.quit(): Деинициализирует все модули Pygame. Это корректный способ завершить работу с библиотекой.quit(): Завершает выполнение Python-скрипта.
Если вы запустите код сейчас, вы увидите статичную картинку со змейкой и едой. Окно будет реагировать на нажатие стрелок (внутри переменная snake_direction будет меняться) и на закрытие. В следующем разделе мы добавим логику движения, чтобы превратить эту статичную сцену в настоящую игру.
Раздел 4: Игровая логика: движение, рост и столкновения
На данный момент у нас есть окно, объекты и обработка ввода. Теперь нам нужно связать их воедино: заставить змейку двигаться в соответствии с нажатиями клавиш, расти при поедании еды и реагировать на столкновения. Вся эта логика будет добавлена в секцию «Обновление состояния» нашего главного игрового цикла.
Шаг 1: Реализация движения
Движение змейки — это простое изменение координат ее головы (snake_pos) в каждом кадре в зависимости от текущего направления (snake_direction). Этот код нужно вставить в игровой цикл сразу после блока обработки событий (for event in...).
# ... после блока for event in pygame.event.get(): ... # 2. Обновление состояния # Обновляем координаты головы змейки if snake_direction == 'UP': snake_pos[1] -= BLOCK_SIZE elif snake_direction == 'DOWN': snake_pos[1] += BLOCK_SIZE elif snake_direction == 'LEFT': snake_pos[0] -= BLOCK_SIZE elif snake_direction == 'RIGHT': snake_pos[0] += BLOCK_SIZE
Шаг 2: Механика роста и обновления тела
Просто изменить координаты головы недостаточно — все тело должно следовать за ней. Для этого используется элегантный алгоритм:
В начало списка
snake_bodyмы добавляем новый сегмент с обновленными координатами головы.Если змейка не съела еду, мы удаляем самый последний сегмент из списка
snake_body.
В результате общая длина змейки остается прежней, но создается полная иллюзия движения всего тела. Если же змейка съела еду, мы просто пропускаем шаг удаления хвоста, и ее длина увеличивается на один сегмент.
Этот код добавляется сразу после блока обновления координат:
# ... после обновления snake_pos ... # Добавляем новую голову в начало тела змейки snake_body.insert(0, list(snake_pos)) # Проверяем, съела ли змейка еду if snake_pos[0] == food_pos[0] and snake_pos[1] == food_pos[1]: # Еда съедена: не удаляем хвост, генерируем новую еду food_spawned = False while not food_spawned: # Генерируем новую позицию new_food_pos = [random.randrange(0, SCREEN_WIDTH // BLOCK_SIZE) * BLOCK_SIZE, random.randrange(0, SCREEN_HEIGHT // BLOCK_SIZE) * BLOCK_SIZE] # Проверяем, что новая еда не появляется на теле змейки if new_food_pos not in snake_body: food_pos = new_food_pos food_spawned = True else: # Еда не съедена: удаляем последний сегмент (хвост) snake_body.pop()
Важный момент: мы добавили проверку, чтобы новая еда не сгенерировалась случайно "внутри" уже существующей змейки.
Шаг 3: Логика проигрыша (обработка столкновений)
Игра должна заканчиваться в двух случаях: при столкновении со стеной или с собственным хвостом. Эти проверки нужно добавить сразу после логики движения и роста.
# ... после логики роста ... # Проверка на столкновение со стенами if snake_pos[0] < 0 or snake_pos[0] >= SCREEN_WIDTH: game_over = True if snake_pos[1] < 0 or snake_pos[1] >= SCREEN_HEIGHT: game_over = True # Проверка на столкновение с собственным телом # Проверяем, совпадает ли голова с какой-либо частью тела (кроме самой головы) for block in snake_body[1:]: if snake_pos[0] == block[0] and snake_pos[1] == block[1]: game_over = True break # Выходим из цикла, если столкновение найдено
Столкновение со стенами: Мы просто проверяем, не вышли ли координаты головы
snake_posза пределы игрового окна.Столкновение с собой: Мы перебираем все сегменты тела змейки, начиная со второго (
snake_body[1:]), и сравниваем их координаты с координатами головы. Если находится совпадение — игра окончена.
Теперь у вас есть полностью играбельная версия «Змейки»! Она движется, растет и корректно обрабатывает условия проигрыша. В последнем разделе мы добавим финальный штрих — отображение счета, чтобы сделать игру завершенной.
Раздел 5: Доработка: счет и вывод текста
Наша игра уже функциональна, но ей не хватает важного элемента — обратной связи для игрока. Отображение счета не только мотивирует играть дальше, но и является отличным поводом изучить, как выводить текст на экран с помощью Pygame.
Шаг 1: Инициализация шрифта и переменной для счета
Для начала нам нужно определить, какой шрифт мы будем использовать. Это делается один раз, в самом начале программы, вместе с другими константами.
Добавьте этот код в секцию с константами в snake_game.py:
# ... после определения цветов ... # Шрифты FONT_STYLE = pygame.font.SysFont('bahnschrift', 25) # Вы можете выбрать другой шрифт
pygame.font.SysFont() создает объект шрифта. Первым аргументом идет название шрифта (выберите любой, установленный в вашей системе, или None для шрифта по умолчанию), вторым — его размер.
Теперь, перед началом главного игрового цикла, инициализируем переменную для хранения счета:
# ... перед `game_over = False` ... score = 0
Шаг 2: Создание функции для отображения счета
Чтобы не загромождать главный цикл, создадим отдельную функцию для рендеринга и отображения текста. Процесс вывода текста в Pygame состоит из двух шагов: сначала создается поверхность с текстом (render), а затем эта поверхность отрисовывается на основном экране (blit).
Добавьте эту функцию в начало файла, после инициализации шрифта:
def show_score(current_score): # Создаем поверхность с текстом score_text = FONT_STYLE.render("Счет: " + str(current_score), True, WHITE) # Отрисовываем текст в левом верхнем углу screen.blit(score_text, [10, 10])
FONT_STYLE.render(): Метод принимает текст, флаг сглаживания (Trueдля более качественного отображения) и цвет текста. Он возвращает новую поверхность (Surface), на которой "нарисован" указанный текст.screen.blit(): Этот метод "копирует" пиксели с одной поверхности на другую. Здесь мы копируем текст с поверхностиscore_textна главный экранscreenпо координатам[10, 10].
Шаг 3: Обновление и отображение счета в игре
Осталось сделать две вещи:
Увеличивать счет каждый раз, когда змейка съедает еду.
Вызывать нашу функцию
show_score()в каждом кадре, чтобы счет всегда был виден.
Найдите в игровом цикле блок, где проверяется столкновение змейки с едой, и добавьте туда score += 1:
# ... в игровом цикле ... # Проверяем, съела ли змейка еду if snake_pos[0] == food_pos[0] and snake_pos[1] == food_pos[1]: score += 1 # Увеличиваем счет # ... остальная логика поедания еды ...
А теперь вызовем функцию отображения. Это нужно делать в секции отрисовки, после заливки фона, но перед pygame.display.flip():
# ... в секции "Отрисовка" игрового цикла ... screen.fill(BLACK) # Рисуем змейку, еду... # ... # Отображаем текущий счет show_score(score) # Обновляем экран pygame.display.flip()
Теперь, когда вы запустите игру, в левом верхнем углу будет отображаться ваш текущий счет, который увеличивается с каждым съеденным блоком.
На этом разработка основной механики «Змейки» завершена. Вы прошли путь от пустого файла до полноценной интерактивной игры, изучив ключевые принципы работы с Pygame.
Заключение и дальнейшие шаги
Поздравляем! Вы успешно прошли полный цикл разработки, превратив пустой файл в законченную и функционирующую игру «Змейка».
Код проекта оставил на github тут.
Анонс новых статей, полезные материалы, а так же если в процессе написания кода возникнут сложности, обсудить их или задать вопрос по статье можно в моём Telegram-сообществе.
Теперь у вас есть прочная база для создания собственных, более сложных проектов с помощью Pygame.
