Как написать свою змейку на Java за 15 минут

Posted by

В предыдущей статье мы писали сапёра за 15 минут, теперь займёмся классической змейкой.

В этот раз нам снова понадобятся:

— 15 минут свободного времени;
— Настроенная рабочая среда, т.е. JDK и IDE (например Eclipse);
— Библиотека LWJGL (версии 2.x.x) для работы с Open GL. Обратите внимание, что для LWJGL версий выше 3 потребуется написать код, отличающийся от того, что приведён в статье;
— Спрайты, т.е. картинки самой змеи и фрукта, который она будет есть. Можно чисто символически нарисовать самому, или скачать использовавшиеся при написании статьи.

Подключение библиотек

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

Во-вторых, у многих пользователей InteliJ IDEA возникли проблемы как раз с их подключением. Я нашёл в сети следующий видеогайд:

После того, как я сделал всё в точности по нему, у меня библиотеки подключились корректно и всё заработало.

Работа с графикой

С этой стороны наша задача мало отличается от той, что мы выполняли при написании Сапёра. Снова создаём класс GUI, который будет хранить и обновлять состояние всех графических элементов. Если точнее:

— Класс будет выполнять инициализацию OpenGL:

— Должен хранить текущие состояния ячеек://Класс Cells напишем несколько позже private static Cell[][] cells;
— Должен отрисовывать эти самые ячейки:///Рисует все клетки public static void draw(){ ///Очищает экран от старого изображения glClear(GL_COLOR_BUFFER_BIT); for(Cell[] line:cells){ for(Cell cell:line){ drawElement(cell); } } }private static void drawElement(Cell elem){ ///Если у ячейки нет спрайта, то рисовать её не нужно if(elem.getSprite() == null) return; ///Собственно, рисуем. Подробно не останавливаюсь, так как нам интересна сама логика игры, а не LWJGL elem.getSprite().getTexture().bind(); glBegin(GL_QUADS); glTexCoord2f(0,0); glVertex2f(elem.getX(),elem.getY()+elem.getHeight()); glTexCoord2f(1,0); glVertex2f(elem.getX()+elem.getWidth(),elem.getY()+elem.getHeight()); glTexCoord2f(1,1); glVertex2f(elem.getX()+elem.getWidth(), elem.getY()); glTexCoord2f(0,1); glVertex2f(elem.getX(), elem.getY()); glEnd(); }
— И обновляться, когда этого запросит главный цикл: //Этот метод будет вызываться извне public static void update(boolean have_to_decrease) { updateOpenGL(); for(Cell[] line:cells){ for(Cell cell:line){ cell.update(have_to_decrease); } } } ///А этот метод будет использоваться только локально, /// т.к. базовым другие классы должны работать на более высоком уровне private static void updateOpenGL() { Display.update(); Display.sync(FPS); }

Как вы можете видеть, здесь я уже использовал несколько констант. Для них был создан отдельный класс Constants с public static полями. Вот он целиком:

Enum Sprite, который отвечает за подгрузку текстур, полностью идентичен тому, что мы писали для Сапёра, за исключением того, что нам нужно только две текстуры — для змеи и для ягод. Вот код:

Механика игры

Самое время поговорить о том, как наша змея будет, собственно, перемещаться. Вам наверняка доводилось видеть вывески, вокруг которых по кругу бегают огоньки? Разумеется, сами лампочки в них не перемещаются, просто каждый тик последняя гаснет, а первая зажигается. Таким же образом будет перемещаться и наша змея.

Несложно подсчитать, что каждая лампочка должна гореть столько тиков, какова длина “змеи”. Значит, мы должны сообщить клетке, в которую попадает змея, что она должна гореть определённое количество секунд, а каждый тик уменьшать это число у каждой клетки с ненулевым таймером, и менять спрайт, если змея из клетки уже выползла (т.е. таймер стал равен нулю). В случае же необходимости удлинить цепочку, достаточно просто не уменьшать время “горения” клеток на каком-то тике. Именно поэтому метод update() у классов Cell и GUI принимает параметр — если он равен false, значит, змея что-то съела.

Пишем класс клетки

Добавляем геттер и сеттер для состояния клетки поля в GUI

Добавляем метод, создающий начальное поле в GUI

Просто инициализируем OpenGL, затем массив Cell[][] cells и заполняем последний клетками со случайным полем state.

Главный управляющий класс

Готово!
P.S. Исходники можно скачать здесь (архив всей папки проекта).

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *