Найти в Дзене
ZDG

Пишем Питона на Питоне #20: Крайние случаи и .exe

Предыдущие части: Доделки, Настройки игры, Игровые уровни, Строим глазки, Таблица рекордов, Шрифт, Остановите музыку, Включите звук, Жизни и модальные окна, Подсчёт очков, Главное меню, Геймплей, Есть ли у него душа, Организация ввода, View, Визуализация поля, Загрузка уровня, INI-файл, Пишем Питона на Питоне!

Код для этой части находится в ветке final на github. Вы можете смотреть там все файлы онлайн и также скачать зип-архив всей ветки.

Игра фактически закончена и работает, однако в ней есть серьёзные проблемы. В этой части сделаем ревизию максимальной длины питона, генерации яблок и начисления баллов игроку. А в конце сделаем исполняемый exe-файл для Windows.

1. Максимальная длина питона

Что, если питон вырастет настолько, что займёт все свободные клетки уровня? Мы отправляем пользователя играть, небезосновательно надеясь, что он никогда не сможет доиграть до такого момента. Но вероятность всегда есть, и представьте что будет, если игрок всё-таки с большим трудом сможет это сделать, а игра зависнет или вылетит с ошибкой, потому что в ней не предусмотрена такая ситуация. Получится очень обидно.

Решение

Количество свободных клеток на уровне мы определили ещё при загрузке уровня. Когда длина питона становится равной этому количеству – значит, свободных клеток больше не осталось. Тогда просто завершим уровень, как будто питон выполз в выход.

Как я проверил корректность решения, ведь для этого надо реально вырастить питона длиной с уровень? Я просто сделал специальный уровень из одного узкого коридора:

2. Генерация яблок

Для яблока генерируются случайные координаты. Если эти координаты попадают на занятое поле, их нужно перегенерировать. Пока питон маленький и большая часть поля пуста, коллизии почти не возникают, то есть мы получаем яблоко максимум с нескольких попыток.

Но по мере того, как питон растёт, он занимает собой всё больше клеток. Если представить ситуацию, когда из 10000 клеток свободна одна, то шанс случайно выбрать именно её будет очень мал и придётся делать большое количество повторений. Это экстремальные условия, но всё же программа должна их учитывать.

Решение

  1. Проходим циклом по всем клеткам поля
  2. Найдя свободную клетку, добавляем её адрес в отдельный список

После того, как мы пройдём по всем клеткам, у нас образуется список адресов свободных клеток. В этом списке можно взять любую случайную позицию и таким образом гарантированно получить свободный адрес.

Да, чтобы поставить яблоко, нам придётся перебирать все клетки поля, но зато мы точно знаем, что это время – константа.

Можно было бы начинать с пустого списка и добавлять в него адреса с помощью append(), но это ведёт к пересозданию объектов в памяти и следовательно лишней нагрузке.

Список мы создадим один раз. Его длина будет равна количеству свободных клеток (как и у буфера питона). Будем не добавлять в этот список элементы, а просто каждый раз заполнять его от начала.

3. Начисление баллов

Пока питон ползает по полю, игрок получает 1 балл за одно перемещение питона. Он может ползать сколько угодно по одному уровню, даже не поедая яблок, и набирая очки. Чтобы избежать такой ситуации, мы добавляем в INI-файл новые параметры для уровней:

  • timer
  • apple_timer
  • growth

Они задаются для каждого уровня индивидуально.

timer – общее время в секундах, которое игрок может провести на уровне. Если это время истекло, а игрок ещё не вышел с уровня, то он проиграл. А выйти с уровня можно только собирая яблоки. Если игрок успел собрать необходимое количество яблок и открыл выход, то в оставшееся время может и просто поползать. Это его дело. А наше дело – задать оптимальный таймер и количество яблок.

apple_timer – время жизни яблока в секундах. Если питон не съел яблоко за это время, то оно исчезает и появляется в другом месте. Если уровень сложный или игрок плохо играет, он будет тратить много времени, чтобы съесть яблоко. Кроме того, за пропавшее яблоко можно штрафовать.

growth – на сколько клеток удлиняется питон, съев яблоко. Этим параметром можно разнообразить геймплей на уровне, заставив питона расти или медленно, или быстро, или вообще не расти.

apple_timer и growth уже в деле в контроллере GameController. А timer я добавил, но пока не использую. Когда у игрока истекает время, нужно будет нарисовать ему модальное окно с сообщением. Для этого придётся сделать ещё представление и контроллер.

4. EXE-файл

Исполняемый файл сделать оказалось довольно просто.

Сначала нужно инсталлировать пакет pyinstaller:

pip install pyinstaller

И затем просто выполнить команду:

pyinstaller --onefile main.py

Хотя всё это породило кучу сообщений, в результате получились каталоги build и dist. В каталоге dist был файл main.exe.

После запуска exe-файла игра работает без проблем. Я включил main.exe в git-проект, но учтите, что для работы ему нужен файл game.ini и каталог data.

Хотел на этой части закончить, но видимо придётся делать ещё 1-2 выпуска.