Как сделать змейку на андроид
Пишем змейку для Android (Часть 1)
Всем привет! Сегодня мы начнем писать змейку для Android.
После достаточно долгого перерыва решил написать еще парочку статей. Дело в том, что владелец одного из сайтов смежной тематики предложил мне написать пару статей для его ресурса. Статьи должны были быть на тему создания какойнить простой игры. Я подумал и решил сделать змейку. Игра простая, но достаточно любопытная! Правда потом адрес владельца этого ресурса у меня куда-то потерялся, а вот сама игрушка осталась. Решил собственно осветить процесс. Игра написана без применения моего движка, так что эту серию статеек можно читать в отрыве от всего остального!
Ну, приступим! Для начала давайте разберемся что такое змейка, в чем смысл этой игры и пр. Значит по экрану устройства бегает изгибающаяся колбаса (собственно змейка!) кушает некие объекты на экране, съев объект начинает расти не по дням а по часам, столкнувшись сама с собой или с каким-то препятствием змея благополучно умирает :(. Возможны различные уровни сложности (ускоряется сама змея или появляется больше препятствий на ее пути). Управлять этой штукой я думаю лучше всего посредством акселерометра.
Думаю мы с Вами не будем внедрять в нее кучу всякого хлама, просто сделаем бесконечно растущую змею, управлять ее будем с помощью акселерометра, препятствием пусть будут только стены. думается человеку который разберется с основными принципами игры не составит никакого труда расширить проект, добавить в него всяких сложностей, и пр.
Начнем пожалуй с реализации логики самого приложения. Для этого опишем вот такой класс SnakeGame- который есть ни что иное, как реализация логики самой игры.
Что мы тут имеем? Начнем с вложенного класса pos. Это просто позиция — два целых числа x и y. Затем имеются константы направлений — ну и собственно сама переменная, в которой хранится направление — mDirection. А самое главное здесь у нас имеется два основных компонента змейки. Перовое (сама змейка) — массив mSnake двумерные координаты каждого сегмента змейки. Второе — само игровое поле mField — двумерный массив, каждый элемент массива кодирует одну клетку игрового поля: -1 это клетка в которой находится змея; 0 — это клетка в которой ничего нет, а 2 — это клетка в которой есть фрукт. Есть так же возможность использовать код 1 — в качестве стенки, но это так, задел на будущее :-).
Конструктор этого класса ничем не примечателен, в конструкторе очищается игровое поле, задается начальное положение змейки. А так же с помощью метода addFruite() добавляется один фрукт на игровое поле. Сам метод тоже ничем не примечателен.
По сути в том классе есть только один достаточно большой метод, разобраться с которым надо поподробнее. Это nextMove() — возвращающий true — если змея может двигаться дальше в направлении указанном в переменной mDirection. Прежде всего определяется в каком именно направлении должна двигаться змейка, затем для каждого направления проверяются такие параметры как «не упрется ли змея в стену», «не съест ли она фрукт», и если съест, то как именно будет расти дальше. В случае, если все проверки прошли удачно, то возвращается истина, если нет, то ложь. В коде достаточно подробно откомментировано каждое действие. Думаю не составит особого труда разобраться что к чему.
Ну вот, с логикой игры вроде разобрались. Теперь приступим к реализации логики самого приложения. Для пущей забавы я решил построить приложение на двух Activity: первая — это активити с меню игры, она же отображает результаты, а вторая — активити с самой игрой. Начнем с первой активити, тем более, что она достаточно простая.
Итак, для этой activity я сделал два разных файла разметки. Первый файл содержит всего одну кнопку, а второй — надписи и кнопку. Вот скриншоты:
А вот сам код класса:
Как видно из кода, мы загружаем файл разметки при показе Actvity и в зависимости от того, что находится в переменной GAME_MODE — файлы разметки разные.
Ну пожалуй хватит на сегодня. В следующий раз будем разбираться с отрисовкой.
Если у Вас есть вопросы или комментарии пишите на мыло или оставляйте в комментах.
Написание змейки для Android на Kivy, Python
[UPD от 2021: этот туториал создан исключительно из-за отсутствия нормальных туториалов по теме на момент написания, а не из побуждения научить мир программистов чему-то правильному]
Много людей хотели бы начать программировать на андроид, но Android Studio и Java их отпугивают. Почему? Потому, что это в некотором смысле из пушки по воробьям. «Я лишь хочу сделать змейку, и все!»
Начнем! (бонус в конце)
Если вы — питонист, и хотите начать разработу простых игр под андроид, вы должно быть уже загуглили «змейка на андроиде» и нашли это (Eng) или ее перевод (Рус). И я тоже так сделал. К сожалению, я нашел статью бесполезной по нескольким причинам:
Плохой код
- Использование «хвоста» и «головы» по отдельности. В этом нет необходимости, так как в змее голова — первая часть хвоста. Не стоит для этого всю змею делить на две части, для которых код пишется отдельно.
- Clock.schedule от self.update вызван из… self.update.
- Класс второго уровня (условно точка входа из первого класса) Playground объявлен в начале, но класс первого уровня SnakeApp объявлен в конце файла.
- Названия для направлений («up», «down», . ) вместо векторов ( (0, 1), (1, 0)… ).
Серьезные недостатки:
- Динамичные объекты (к примеру, фрукт) прикреплены к файлу kv, так что вы не можете создать более одного яблока не переписав половину кода
- Чудная логика перемещения змеи вместо клетка-за-клеткой.
- 350 строк — слишком длинный код.
Статья неочевидна для новичков
Это мое ЛИЧНОЕ мнение. Более того, я не гарантирую, что моя статья будет более интересной и понятной. Но постараюсь, а еще гарантирую:
- Код будет коротким
- Змейка красивой (относительно)
- Туториал будет иметь поэтапное развитие
Результат не комильфо
Знакомство
Первое приложение
Пожалуйста, удостовертесь в том, что уже установили Kivy (если нет, следуйте инструкциям) и запустите
buildozer init в папке проекта.
Запустим первую программу:
Мы создали виджет. Аналогично, мы можем создать кнопку или любой другой элемент графического интерфейса:
Ура! Поздравляю! Вы создали кнопку!
Файлы .kv
Однако, есть другой способ создавать такие элементы. Сначала объявим нашу форму:
Затем создаем «worm.kv» файл.
Что произошло? Мы создали еще одну кнопку и присвоим id but_id. Теперь but_id ассоциировано с but2 формы. Это означает, что мы можем обратиться к button с помощью but2:
Графика
Далее создадим графический элемент. Сначала объявим его в worm.kv:
Мы связали позицию прямоугольника с self.pos и его размер с self.size. Так что теперь эти свойства доступны из Cell, например, как только мы создаем клетку, мы можем менять ее размер и позицию:
Окей, мы создали клетку.
Необходимые методы
Давайте попробуем двигать змею. Чтобы это сделать, мы можем добавить функцию Form.update и привязать к расписанию с помощью Clock.schedule.
Клетка будет двигаться по форме. Как вы можете видеть, мы можем поставить таймер на любую функцию с помощью Clock.
Далее, создадим событие нажатия (touch event). Перепишем Form:
Каждый touch_down создает клетку с координатами = (touch.x, touch.y) и размером = 30. Затем, мы добавим ее как виджет формы И в наш собственный массив (чтобы позднее обращаться к нему).
Теперь каждое нажатие на форму генерирует клетку.
Няшные настройки
Так как мы хотим сделать красивую змейку, мы должны логически разделить графическую и настоящую позиции.
Много причин делать это. Вся логика должна быть соединена с так называемой настоящей позицией, а вот графическая — есть результат настоящей. Например, если мы хотим сделать отступы, настоящая позиция будет (100, 100) пока графическая — (102, 102).
P. S. Мы бы этим не парились если бы имели дело с on_draw. Но теперь мы не обязаны перерисовать форму лапками.
Давайте изменим файл worm.kv:
Появился отступ, так что это выглядит лучше не смотря на то, что мы создали вторую клетку с X = 130 вместо 132. Позже мы будем делать мягкое передвижение, основанное на расстоянии между actual_pos и graphical_pos.
Программирование червяка
Объявление
Инициализируем config в main.py
(Поверьте, вы это полюбите!)
Затем присвойте config приложению:
Перепишите init и start:
Надеюсь, это было более менее понятно.
Давайте создадим нашего червячка.
Движение
Теперь подвигаем ЭТО.
Оно живое! Оно живое!
Управление
Как вы могли судить по первой картинке, управление змеи будет таким:
Создание фрукта
Теперь мы должны объявить несколько методов Worm:
Кстати, после того, как мы объявили gather_positions, мы можем улучшить fruit_dislocate:
На этот моменте позиция яблока не сможет совпадать с позиции хвоста
… и добавим проверку в update()
Определение пересечения головы и хвоста
Мы хотим узнать та же ли позиция у головы, что у какой-то клетки хвоста.
Раскрашивание, декорирование, рефакторинг кода
Начнем с рефакторинга.
Перепишем и добавим
Теперь если червяк мертв (заморожен), если вы нажмете на экран, игра будет начата заново.
Теперь перейдим к декорированию и раскрашиванию.
Раскрасим. Перепишем Cell in .kv:
Добавим это к Cell.__init__:
и это к Form.start
Превосходно, наслаждайтесь змейкой
Наконец, мы создадим надпись «game over»
И зададим «раненой» клетке красный цвет:
Вы еще тут? Самая интересная часть впереди!
Бонус — плавное движение
Так как шаг червячка равен cell_size, выглядит не очень плавно. Но мы бы хотели шагать как можно чаще без полного переписывания логики игры. Таким образом, нам нужен механизм, который двигал бы наши графические позиции (graphical_pos) но не влиял бы на настоящие (actual_pos). Я написал следующий код:
Так, вы лишь создаете smooth.py and и копируете код в файл.
Наконец, заставим ЭТО работать!
Заменим self.worm.move() с
А это как методы Cell должны выглядить
Ну вот и все, спасибо за ваше внимание! Код снизу.
Демонстрационное видео как работает результат:
Как написать свою змейку на Java за 15 минут
В предыдущей статье мы писали сапёра за 15 минут, теперь займёмся классической змейкой.
В этот раз нам снова понадобятся:
- 15 минут свободного времени;
- Настроенная рабочая среда, т.е. JDK и IDE (например Eclipse);
- Библиотека LWJGL (версии 2.x.x) для работы с Open GL. Обратите внимание, что для LWJGL версий выше 3 потребуется написать код, отличающийся от того, что приведён в статье;
- Спрайты, т.е. картинки самой змеи и фрукта, который она будет есть. Можно чисто символически нарисовать самому, или скачать использовавшиеся при написании статьи.
Подключение библиотек
В прошлый раз у многих возникли с этим вопросом проблемы, поэтому мне показалось уместным посвятить этому немного времени. Во-первых, выше я дал ссылку на скачивание архива с библиотеками, которые использую я, чтобы не было путаницы с версиями и вопросов, где найти что-то. Папку из архива требуется поместить в папку проекта и подключить через вашу IDE.
Во-вторых, у многих пользователей InteliJ IDEA возникли проблемы как раз с их подключением. Я нашёл в сети следующий видеогайд:
После того, как я сделал всё в точности по нему, у меня библиотеки подключились корректно и всё заработало.
Работа с графикой
С этой стороны наша задача мало отличается от той, что мы выполняли при написании Сапёра. Снова создаём класс GUI, который будет хранить и обновлять состояние всех графических элементов. Если точнее:
- Класс будет выполнять инициализацию OpenGL:
- Должен хранить текущие состояния ячеек:
- Должен отрисовывать эти самые ячейки:
- И обновляться, когда этого запросит главный цикл:
Как вы можете видеть, здесь я уже использовал несколько констант. Для них был создан отдельный класс Constants с public static полями. Вот он целиком:
Enum Sprite , который отвечает за подгрузку текстур, полностью идентичен тому, что мы писали для Сапёра, за исключением того, что нам нужно только две текстуры — для змеи и для ягод. Вот код:
Механика игры
Самое время поговорить о том, как наша змея будет, собственно, перемещаться. Вам наверняка доводилось видеть вывески, вокруг которых по кругу бегают огоньки? Разумеется, сами лампочки в них не перемещаются, просто каждый тик последняя гаснет, а первая зажигается. Таким же образом будет перемещаться и наша змея.
Несложно подсчитать, что каждая лампочка должна гореть столько тиков, какова длина «змеи». Значит, мы должны сообщить клетке, в которую попадает змея, что она должна гореть определённое количество секунд, а каждый тик уменьшать это число у каждой клетки с ненулевым таймером, и менять спрайт, если змея из клетки уже выползла (т.е. таймер стал равен нулю). В случае же необходимости удлинить цепочку, достаточно просто не уменьшать время «горения» клеток на каком-то тике. Именно поэтому метод update() у классов Cell и GUI принимает параметр — если он равен false , значит, змея что-то съела.
Пишем класс клетки
Добавляем геттер и сеттер для состояния клетки поля в GUI
Добавляем метод, создающий начальное поле в GUI
Просто инициализируем OpenGL, затем массив Cell[][] cells и заполняем последний клетками со случайным полем state .