Сегодня мы напишем игру змейка на Python. Это одна из самых распространенных аркад в мире. В основе ее реализации будет два класса:
- Класс сегмента (одного блока змейки).
- Класс змеи (по сути собрание сегментов).
В процессе написания змейки у нас будут следующие этапы:
Создание графического окна приложения.
Мы будем работать с библиотекой tkinter, в котором окно задается следующим образом:
Обратите внимание, что весь остальной код должен находиться до строки root.mainloop()
Объявление вспомогательных глобальных переменных.
Тут все просто, для облегчения последующей работы создадим следующие переменные:
Установка на окне области для рисования.
Область для рисования в tkinter реализована при помощи класса Canvas, им и воспользуемся.
Если вы все делали правильно, то запустив полученный код получите следующую картину

Создание классов сегмента и змеи:
Класс сегмента змейки.
Сегмент змейки будет простым прямоугольником, созданным при помощи метода create_rectangle класса Canvas модуля tkinter.
Класс змейки.
Змейка у нас будет набором сегментов. У нее будут методы движения, изменения направления и добавления сегмента.
Если вы осилили эти два класса и более того, поняли что, как и почему, то поздравляю — самая трудная часть позади. Уже сейчас можно создавать змейку. Вставте следующие строчки, но обязательно после строк c.grid()
Вот так выглядит наша игра на данный момент.

Создание вспомогательных функций.
Для начала напишем функцию создания яблок (или что там наша змея будет есть). Не забудьте импортировать модуль random, чтобы все работало
Теперь основная функция main, которая будет управлять игровым процессом.
Последний шаг — правильно обработать нажатия клавиш. Привяжем метод класса Snake change_direction() к Canvas:

Вот такая у нас получилась змейка на Python. Попробуйте немного усовершенствовать игру, например, добавить счет, ускорение игры, отредактировать функцию create_block чтобы новые яблоки не появлялись на самой змейке, или реализовать систему уровней.
UPD: В репозитории добавлен вариант с перезапуском игры после проигрыша.
Блог питониста
Есть такая библиотека для разработки двухмерных игр — pygame. Недавно я прошел курс на образовательном сайте udemy — Python Game Development : Creating a Snake Game from scratch. Этот курс посвещен созданию классической змейки на pygame. Хотел бы поделиться получившейся игрой. Весь код в данном посте я взял из данного курса, правда изрядно его изменил, в частности добавил использование классов. Также я внес несколько исправлений.
Установить Pygame можно через pip:
pip install pygame
Начнем с импортов:
Будем использовать собственно pygame, sys ради функции exit — чтобы завершать скрипт при ошибке или game over'e, random — ради функции randrange, чтобы размещать еду в случайных местах и time — ради функции sleep, чтобы экран игры немного задержался при game over'e.
Я разбил игру на 3 класса: Game — в котором находятся настройки игры, методы для инициализации, завершения игры, отрбражения результата, Snake — здесь параметры отвечающие за позицию головы змеи, координаты тела змеи, метод, который обеспечивает изменения тела (добавления и убирания сегментов) и Food — параметр положения еды, метод отображения еды.
Я добавил побольше комментариев, чтобы объяснить код. На строке 25 в переменной check_errors будет кортеж вида (6, 0) — (количество завершенных тасков, количество ошибок), поэтому на строке 26 берется второй элемент кортежа и проверяется на количество ошибок. В функции event_loop и в фукции game_over я сначала убиваю окно pygame функцией quit(), а затем уже выхожу из скрипта sys.exit'ом. В функции show_score переменная choice нужна чтобы определить как выводить результат — слева сверху в течение игры (стр. 72) или по центру в момент завершения (стр. 76).
Далее идет класс Snake:
В конструкторе класса стр.2-стр.7 задаются две переменные, отвечающие за положение змеи (головы и сегментов). В функции validate_direction_and_change направление движения змеи изменяется только тогда, когда игрок не нажал противоположное направление — так нельзя делать в классической змейке, поворачивать можно только под прямым углом. В функции snake_body_mechanism, в которой описан механизм изменения тела змеи, есть важный момент — в строке 42 обязательно создавать новый список (делать list(self.snake_head_pos)), иначе (если сделать просто self.snake_body.insert(0, self.snake_head_pos) ) будет передан не новый список, а тот же самый список snake_head_pos и через несколько итераций мы будем управлять одной точкой, после чего игра завершится game_over'ом, предлагаю вам проверить это самим и подумать почему так получается.
Позиция еды на строке 48 задается таким образом, чтобы x и y еды были всегда кратны 10, чтобы всегда была возможность ее захватить. Начало координат находится в левом верхнем углу, причем ось x напрвлено вправо, а ось y — вниз, поэтому на строках 28, 30, 32 и 34 координаты изменяются соответствующим образом.
Тут все понятно, единственное, в методе draw_food используется метод rect(), который принимает 3 аргумента: поверхность, цвет и объект, в данном случае Rect(x,y, sizex, sizey).
Далее создаем классы и инициализиурем Pygame:
Далее запускаем бесконечный цикл, в котором последовательно вызыаем описанные методы классов с соответствующими параметрами:
Заключение
Данную игру можно улучшить, добавив изображения, музыку, еще ей не хватает настроек и меню, а также настроек сложности. Если вы хотите поделиться ею с друзьями, то, возможно, вы захотите скомпилировать ее в exe. Для этой цели можно использовать, например, pyinstaller:
Пишем «Змейку» под Android на Python и Kivy
В этой статье мы напишем классическую «Змейку» на Python с помощью инструмента для создания GUI Kivy.
Знакомимся с Kivy
Kivy — это популярный инструмент для создания пользовательских интерфейсов, который отлично подходит для разработки приложений и несложных игр. Одним из основных достоинств Kivy является портируемость — возможность безболезненного переноса ваших проектов с одной платформы на другую. Наша «Змейка» будет работать на платформе Android.
Kivy эффективно использует Cython — язык программирования, сочетающий в себе оптимизированность C++ и синтаксис Python — что положительно сказывается на производительности. Также Kivy активно использует GPU для графических процессов, освобождая CPU для других вычислений.
Рекомендуемые ресурсы для начала работы с Kivy:
Устанавливаем Kivy
Зависимости
Прим. перев. Код проверен на Ubuntu 16.04, Cython 0.25, Pygame 1.9.4.dev0, Buildozer 0.33, Kyvi 1.10.
Для правильной работы Kivy нам требуется три основных пакета: Cython, pygame и python-dev. Если вы используете Ubuntu, вам также может понадобиться библиотека gstreamer, которая используется для поддержки некоторых видеовозможностей фреймворка.
Устанавливаем зависимости pygame:
Добавляем репозиторий Kivy:
Buildozer
Этот пакет нам понадобится для упрощения процесса установки нашего Android-приложения:
Нам также понадобится Java JDK. И если вы используете 64-битную систему, вам понадобятся 32-битные версии зависимостей.
Устанавливаем Java JDK:
Устанавливаем 32-битные зависимости:
Оно работает?
Прежде чем начать писать нашу «Змейку», давайте проверим, правильно ли у нас все установилось. Иначе в дальнейшем может обнаружиться, что проект не компилируется из-за какого-нибудь недостающего пакета.
Для проверки напишем старый добрый «Hello, world!».
Приступим к созданию проекта. Нужно перейти в рабочую папку и выполнить команду:
Теперь откроем файл с расширением .spec в любом текстовом редакторе и изменим следующие строки:
- имя нашего приложения title = Hello World ;
- название пакета package.name = helloworldapp ;
- домен пакета (нужен для android/ios сборки) package.domain = org.helloworldapp ;
- закомментируйте эти строки, если они ещё не закомментированы:
- строка version = 1.0.0 должна быть раскомментированной.
Создайте файл main.py и добавьте в него следующий код:
Теперь все готово к сборке. Вернемся к терминалу.
Примечание В случае возникновения каких-либо ошибок установите значение log_level = 2 в файле buildozer.spec. Это даст более развернутое описание ошибки. Теперь мы точно готовы приступить к написанию «Змейки».
Прим. перев. Обратите внимание, что в проекте используется один файл и в статье разбираются лишь куски кода из этого файла. Весь исходный код вы можете просмотреть, перейдя по ссылке на страницу проекта в gitHub.
В этой части урока мы напишем игровой движок нашей «Змейки». И под созданием игрового движка подразумевается:
1. Написание классов, которые станут скелетом нашего приложения.
2. Предоставление им правильных методов и свойств, чтобы мы могли управлять ими по своему усмотрению.
3. Соединение всего в основном цикле приложения.
Классы
Теперь давайте разберем нашу игру на составные элементы: змея и игровое поле. Змея состоит из двух основных элементов: головы и хвоста. И надо не забыть, что змее нужно что-то есть.
Таким образом нам потребуется организовать следующую иерархию виджетов:
Мы объявим наши классы в файлах main.py и snake.kv , чтобы отделить дизайн от логики:
Свойства
Теперь, когда мы реализовали классы, можно задуматься о содержимом.
Playground — это корневой виджет. Мы разделим его на сетку. Эта матрица поможет позиционировать и перемещать объекты по полю. Представление каждого дочернего виджета будет занимать одну клетку. Также нужно реализовать возможность сохранения счета и изменения частоты появления фруктов.
И последнее, но не менее важное: нужно реализовать управление вводом, но сделаем мы это в следующем разделе.
Объект змеи не должен содержать ничего, кроме двух ее деталей: головы и хвоста.
Для головы мы должны знать текущее положение и направление движения для правильного графического представления: если змея движется направо — рисуем треугольник повернутый вправо, если змея движется влево — рисуем треугольник повернутый влево.
Позиция и направление будут соответствовать определенным инструкциям рисования. Для рисования треугольника нам нужно шесть точек (две координаты на одну вершину). Эти координаты будут не ячейками сетки, а определенными пикселями на холсте
Наконец, нам надо хранить информацию об объекте, нарисованном на холсте, чтобы удалить его позже (например, для перезапуска игры). Мы добавим логическую переменную, указывающую, должен ли объект быть нарисован на холсте:
Теперь хвост. Он состоит из блоков (изначально трех), занимающих одну ячейку. Когда «Змейка» будет двигаться, мы будем убирать самый последний блок хвоста и добавлять новый на предыдущую позицию головы:
Фрукт
Поведение фрукта похоже на поведение змеиной головы. Нам понадобится отслеживать его позицию, состояние и интервал появления:
В классе SnakeApp будет происходить запуск нашего приложения:
Кое-что еще: нужно задать размеры виджетов. Каждый элемент будет занимать одну ячейку поля. Значит:
- высота виджета = высота поля / количество строк сетки;
- ширина виджета = ширина поля / количество колонок сетки.
Также нам нужно добавить виджет отображающий текущий счет.
Теперь snake.kv выглядит так:
Методы
Начнем с класса змеи. Мы хотим устанавливать ее в начальное положение и получать информацию о том, где она находится и как взаимодействует с объектами поля:
Мы назвали ряд методов. Теперь давайте их реализуем. Начнем с remove() и add_block() :
Теперь работаем с головой. Она будет иметь две функции: move() и remove() :
А что там с фруктами? Мы должны уметь помещать их в заданные координаты и удалять, когда нам это понадобится:
Почти готово, не сдавайтесь! Теперь нужно организовать весь игровой процесс, который будет происходить в классе Playground . Рассмотрим логику игры: она начинается с того, что змея помещается в случайные координаты. Игра обновляется при каждом перемещении змеи. Во время обновлений мы проверяем направление змеи и ее положение. Если змея сталкивается сама с собой или выходит за пределы поля — мы засчитываем поражение и игра начинается сначала.
Как будет осуществляться управление? Когда игрок касается экрана, мы сохраняем координаты касания. Когда палец будет перемещаться, мы будем сравнивать новое положение с исходным. Если позиция будет изменена на 10 % от размера экрана, мы будем определять это как инструкцию и обрабатывать ее:
Основной цикл
Здесь происходят процессы, устанавливающие положение фрукта, управляющие движением змеи и определяющие проигрыш:
Нужно добавить обработчик для события сброса игры:
Теперь мы можем протестировать игру.
Одна важная деталь. Чтобы приложение запустилось с правильным разрешением экрана, нужно сделать так:
И вуаля! Теперь вы можете запустить приложение. Остается только упаковать его с помощью buildozer и загрузить на устройство.

Создаем экраны
В приложении будет два экрана: приветствия и игровой. Также будет всплывающее меню настроек. Сначала мы сделаем макеты наших виджетов в .kv-файле, а потом напишем соответствующие классы Python.
Внешняя оболочка
PlaygroundScreen содержит только игровое поле:
Основной экран будет состоять из трех встроенных виджетов: название приложения, кнопки запуска игры и кнопки вызывающая меню настроек:
Всплывающее окно будет занимать ¾ экрана приветствия. Оно будет содержать виджеты, необходимые для установки параметров, и кнопку «Сохранить».
Классы
На экране приветствия требуется только метод show_popup() , который будет вызываться при нажатии кнопки настроек на главном экране. Нам не нужно определять что-либо еще для кнопки включения игры, потому что она будет использовать способность своего родителя обращаться к диспетчеру экрана:
Теперь нужно сделать так, чтоб экран приветствия показывался при запуске игры, а игра начиналась только тогда, когда будет показано игровое поле:
Также нам нужно подготовить класс окна настроек, иначе привязка будет работать не правильно:
Теперь добавим ScreenManager в приложение и зарегистрируем два экрана:
Теперь нужно сказать кнопкам, что делать, когда на них нажимают:
После проигрыша нужно возвращаться обратно на экран приветствия:
Добавляем настройки
У нас будет всего два параметра:
- Включение/отключение границ. Если границы включены, при выхождении змеи за пределы экрана засчитывается проигрыш. Если границы выключены, змея будет появляться на другой стороне, если выходит за пределы.
- Скорость змеи.
Добавляем необходимые виджеты во всплывающее окно:
Теперь подготовим классы, чтобы они могли изменяться вместе с настройками. Если границы включены, будем рисовать очертание игрового поля. Также добавим возможность изменения частоты обновления:
