В прошлой статье мы познакомились с кодом, настроили экран и подготовили всё необходимое для игры. Мы установили цвета, задали размеры окна, выбрали скорость и даже создали наш первый волшебный ящик с инструментами — библиотеку pygame. А теперь самое интересное: мы наконец-то добрались до сердца игры — до того, как змейка начинает ползать, есть яблоки и реагировать на нажатия клавиш.
Эта часть кода — самая важная. Именно здесь рождается магия. Если вы уже прочитали первую статью, то сейчас мы просто берём те самые кирпичики, которые подготовили, и начинаем строить из них настоящую игру. Если вы новичок и только начинаете изучение языка программирования Python, не переживайте — мы разберём каждую строчку так, будто мы сидим рядом и объясняем всё на пальцах. Никакой магии, только логика и немного терпения.
Что мы уже знаем из первой статьи?
Напомню, что в прошлый раз мы:
- Импортировали библиотеки — взяли волшебные ящики с инструментами.
- Задали цвета — выбрали краски для рисования.
- Создали игровое окно — натянули холст для нашей картины.
- Установили размер и скорость нашего героя — решили, каким будет наш герой и как быстро он будет двигаться.
А теперь мы переходим к написанию самой игры — её души, её внутреннего двигателя.
Начало кода: где появляется змейка и её первая еда
Давайте посмотрим на первые строчки, которые нам предстоит написать:
x1 = dis_width // 2
y1 = dis_height // 2
x1_change = 0
y1_change = 0
Что здесь происходит? Представьте, что вы ставите фигурку на игровое поле. x1 и y1 — это координаты головы змеи. Мы размещаем её ровно посередине экрана: ширина и высота делятся пополам. x1_change и y1_change — это направление движения. Пока основной спрайт стоит на месте, эти значения равны нулю. Как только мы нажмём клавишу, они изменятся, и змейка поползёт! Этот механизм лежит в основе всей игры. Именно так мы управляем движением, и именно здесь начинается изучение языка Python на практике.
snake_list = []
snake_length = 1
Здесь мы создаём пустой список для хранения всех частей тела спрайта. Пока в нём ничего нет, потому что змейка только появилась. Но каждый раз, когда мы будем двигаться, мы будем добавлять новую голову.
snake_length = 1 — это начальная длина. Она будет расти, когда мы будем есть еду. Это как если бы мы собирали поезд: сначала один вагон, потом он становится длиннее.
foodx = random.randrange(0, dis_width - snake_block, snake_block)
foody = random.randrange(0, dis_height - snake_block, snake_block)
А теперь создадим первую еду!
В начале программы мы подключили модуль random. Он умеет выбирать случайные числа, благодаря чему еда каждый раз появляется в новом месте.
Разберём первую строку по частям:
foodx — это переменная, в которой будет храниться координата еды по горизонтали (ось X).
random.randrange(...) — команда, которая случайным образом выбирает число из заданного диапазона.
Внутри скобок находятся три значения:
random.randrange(0, dis_width - snake_block, snake_block)
Что они означают?
0 — начинаем выбирать координату с самого левого края экрана.
dis_width - snake_block — заканчиваем перед правым краем экрана. Мы вычитаем размер одного квадратика (snake_block), чтобы еда не оказалась частично за пределами игрового поля.
snake_block — шаг, с которым выбирается координата. Если размер одного квадрата равен 10 пикселям, то координаты будут такими: 0, 10, 20, 30, 40 и так далее.
Почему это важно?
Наша змея тоже двигается именно такими шагами — по 10 пикселей. Если бы яблоко могло появиться, например, на координате 27,мы бы никогда не смогли бы попасть точно в эту точку. Поэтому и змейка, и еда всегда располагаются на одной и той же "сетке".
Вторая строка работает абсолютно так же:
foody = random.randrange(0, dis_height - snake_block, snake_block)
Только теперь выбирается координата по вертикали (ось Y).
В результате у нашей еды появляются две координаты — X и Y, а значит, программа знает, где именно нарисовать красное яблоко на игровом поле. Каждый запуск игры эти координаты будут разными, поэтому яблоко будет появляться в случайном месте.
Главный игровой цикл: сердце программы
Теперь переходим к самому важному — к бесконечному циклу, который работает, пока игра запущена:
running = True
while running:
Это как включённый двигатель. Цикл while работает по определенному условию. Когда мы пишем просто while running, мы буквально говорим, что пока значение переменной running равно True, цикл продолжает выполняться., мы будем выполнять код. Иными словами, пока running = True, игра продолжает работать. Каждый раз программа проверяет, не произошло ли какое-нибудь событие: нажатие клавиши или закрытие окна, не случилось ли что-то важное. Если вы только начинаете изучение программирования на Python, то такие простые, но важные моменты — это ваша база.
Управление: как змейка слушается клавиш
Внутри цикла мы обрабатываем события — то, что делает игрок:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT and x1_change == 0:
x1_change = -snake_block
y1_change = 0
elif event.key == pygame.K_RIGHT and x1_change == 0:
x1_change = snake_block
y1_change = 0
elif event.key == pygame.K_UP and y1_change == 0:
y1_change = -snake_block
x1_change = 0
elif event.key == pygame.K_DOWN and y1_change == 0:
y1_change = snake_block
x1_change = 0
Представьте, что вы сидите за рулём. Когда вы нажимаете на стрелку влево, машина поворачивает. То же самое здесь: мы проверяем, какую клавишу нажал игрок, и меняем направление. При этом есть важное правило: змейка не может развернуться сама в себя. Например, если она едет влево, она не может мгновенно поехать вправо — мы это запрещаем условием x1_change == 0. Это помогает избежать ошибок, когда мы случайно «врезаемся» в самих себя.
Движение: шаг за шагом
Следующая строка — это то, как мы перемещаемся:
x1 += x1_change
y1 += y1_change
Каждый раз, когда мы проходим через цикл, голова змейки сдвигается на один шаг в том направлении, куда мы её направили. Это как если бы мы переставляли фигурку на клетку вперёд. Если мы нажали «вверх», то y1_change стал отрицательным, и голова поднимается.
Столкновение со стенами: игра заканчивается
if x1 >= dis_width or x1 < 0 or y1 >= dis_height or y1 < 0:
running = False
Это проверка: если голова вышла за границы экрана, игра заканчивается. Для программирования для детей на Python такие простые решения — лучший способ освоить базовые концепции без лишней путаницы. Мы просто выключаем наш двигатель — running = False. В реальной жизни это выглядит как «Game Over». Вы можете добавить сообщение об этом позже, но пока мы просто завершаем игру.
Отрисовка: рисуем мир
dis.fill(blue)
pygame.draw.rect(dis, red, [foodx, foody, snake_block, snake_block])
Мы заливаем экран голубым цветом (помните, мы выбрали его в самом начале?). Это как если бы мы закрашивали фон акварелью, чтобы стереть предыдущий кадр и нарисовать новый. Затем рисуем красный квадратик — это наша еда.
Обновление: как становиться длиннее
snake_head = [x1, y1]
snake_list.append(snake_head)
if len(snake_list) > snake_length:
del snake_list[0]
Это один из самых интересных моментов во всей игре. Именно благодаря этим нескольким строчкам мы научимся двигаться и расти.
Вспомним, что snake_list — это список, в котором хранятся координаты всех частей тела змейки. Представьте, что это обычный список вагончиков поезда. Каждый вагон — это один зелёный квадратик, который мы рисуем на экране.
Когда игра только начинается, в списке находится всего один элемент — голова.
Каждый раз, когда змея делает шаг, её голова оказывается в новом месте. Поэтому сначала мы создаём новую голову:
snake_head = [x1, y1]
Здесь мы просто записываем текущие координаты головы в небольшой список.
После этого добавляем его в конец snake_list:
snake_list.append(snake_head)
Команда append() означает «добавить элемент в конец списка».
Например, если раньше список выглядел так:
[[100, 100]]
то после того как мы сделали шаг вправо, он станет таким:
[[100, 100], [110, 100]]
Получается, что теперь у змейки как будто две головы. Конечно, так быть не должно.
Поэтому сразу после этого мы проверяем длину списка:
if len(snake_list) > snake_length:
Функция len() считает, сколько элементов находится в списке.
В начале игры переменная snake_length равна 1, потому что змейка состоит всего из одного квадратика.
Если элементов стало больше, значит нужно убрать самый старый из них — хвост.
Именно это делает команда:
del snake_list[0]
Она удаляет первый элемент списка. Именно в нём хранится самая старая позиция.
В результате получается интересный эффект: спереди постоянно появляется новая голова, а сзади исчезает хвост. Нам кажется, что змейка ползёт вперёд, хотя на самом деле программа всего лишь каждый кадр добавляет один сегмент и удаляет другой.
А что происходит, когда мы съедаем яблоко?
В этот момент переменная snake_length увеличивается на единицу. Теперь программа разрешает списку стать длиннее, поэтому хвост больше не удаляется сразу. В результате после каждого съеденного яблока тело становится на один квадратик длиннее.
Столкновение с собой: ещё один способ проиграть
for segment in snake_list[:-1]:
if segment == snake_head:
running = False
Здесь мы проверяем: не столкнулась ли голова с каким-нибудь из сегментов тела? Мы проходим по всему списку, кроме последнего элемента (головы), и если находим совпадение — игра заканчивается. Это предотвращает ситуацию, когда змейка сама себя «съедает». В информатике в 8 классе такие алгоритмы часто разбирают на уроках, чтобы показать, как можно управлять коллекциями объектов и создавать динамические системы.
Рисуем змейку: выводим на экран
for segment in snake_list:
pygame.draw.rect(dis, green, [segment[0], segment[1], snake_block, snake_block])
Здесь мы просто проходим по всем сегментам змейки и рисуем их зелёным цветом. Каждый сегмент — это маленький квадратик. Вместе они образуют длинную зелёную линию.
Обновление экрана и проверка еды
pygame.display.update()
Эта команда говорит компьютеру: «Покажи всё, что мы нарисовали!». Без неё экран не обновлялся бы, и мы не видели бы движения. А дальше — проверка на съеденную еду:
if x1 == foodx and y1 == foody:
foodx = random.randrange(0, dis_width - snake_block, snake_block)
foody = random.randrange(0, dis_height - snake_block, snake_block)
snake_length += 1
Если голова совпала с координатами еды, мы генерируем новую еду в случайном месте и увеличиваем длину на 1. Это и есть тот самый момент, когда змейка «растёт».
Регулировка скорости
clock.tick(snake_speed)
Это наш волшебный будильник! Он говорит игре: «Подожди немного, дай время на движение». Если поставить скорость 30, змейка будет бегать очень быстро. Если 10 — она будет еле ползти. Мы выбрали 15 — золотую середину.
Завершение игры
pygame.quit()
Когда цикл заканчивается (мы проиграли или закрыли окно), мы выходим из игры. Это как если бы мы выключили приставку после окончания игры.
Что вы теперь умеете?
Вы только что разобрали полный код игры «Змейка» на Python! Вы знаете, как работает игровой цикл, как обрабатываются нажатия клавиш, как движется змейка, как она ест и растёт, как проверяются столкновения. Это целый набор навыков, которые пригодятся вам не только в этой игре, но и в любых других проектах. Это отличный пример практики и задач: вы не просто читаете теорию, а видите, как код оживает. И если вы хотите научиться не только играть, но и создавать игры самостоятельно, то такие разборы — ваш лучший друг.
Для самопроверки, ниже мы оставим полный код игры и вы сможете убедиться, что всё написано верно.
В онлайн-школе «Матрица» мы учим детей программировать на Python с нуля именно на таких проектах: сначала игры, потом веб-сайты, потом телеграм-боты. Это отличный способ закрепить знания и сразу видеть результат. Наши ученики не зубрят теорию — они создают, пробуют, ошибаются и снова пробуют. Это называется практика и задачи, и это самый эффективный способ обучения.