В предыдущей статье мы писали сапёра за 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. Исходники можно скачать здесь (архив всей папки проекта).
Добавить комментарий