Как написать свою змейку на Java за 15 минут
В предыдущей статье мы писали сапёра за 15 минут, теперь займёмся классической змейкой.
В этот раз нам снова понадобятся:
- 15 минут свободного времени;
- Настроенная рабочая среда, т.е. JDK и IDE (например Eclipse);
- Библиотека LWJGL (версии 2.x.x) для работы с Open GL. Обратите внимание, что для LWJGL версий выше 3 потребуется написать код, отличающийся от того, что приведён в статье;
- Спрайты, т.е. картинки самой змеи и фрукта, который она будет есть. Можно чисто символически нарисовать самому, или скачать использовавшиеся при написании статьи.
Подключение библиотек
В прошлый раз у многих возникли с этим вопросом проблемы, поэтому мне показалось уместным посвятить этому немного времени. Во-первых, выше я дал ссылку на скачивание архива с библиотеками, которые использую я, чтобы не было путаницы с версиями и вопросов, где найти что-то. Папку из архива требуется поместить в папку проекта и подключить через вашу IDE.
Во-вторых, у многих пользователей InteliJ IDEA возникли проблемы как раз с их подключением. Я нашёл в сети следующий видеогайд:
После того, как я сделал всё в точности по нему, у меня библиотеки подключились корректно и всё заработало.
Работа с графикой
С этой стороны наша задача мало отличается от той, что мы выполняли при написании Сапёра. Снова создаём класс GUI, который будет хранить и обновлять состояние всех графических элементов. Если точнее:
- Класс будет выполнять инициализацию OpenGL:
initializeOpenGL()
///Class GUI private static void initializeOpenGL() < try < //Задаём размер будущего окна Display.setDisplayMode(new DisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT)); //Задаём имя будущего окна Display.setTitle(SCREEN_NAME); //Создаём окно Display.create(); >catch (LWJGLException e) < e.printStackTrace(); >glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0,SCREEN_WIDTH,0,SCREEN_HEIGHT,1,-1); glMatrixMode(GL_MODELVIEW); /* * Для поддержки текстур */ glEnable(GL_TEXTURE_2D); /* * Для поддержки прозрачности */ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* * Белый фоновый цвет */ glClearColor(1,1,1,1); >
Как вы можете видеть, здесь я уже использовал несколько констант. Для них был создан отдельный класс Constants с public static полями. Вот он целиком:
public class Constants < ///Размер игровой ячейки public static final int CELL_SIZE = 32; ///Размеры игрового поля в ячейках public static final int CELLS_COUNT_X = 20; public static final int CELLS_COUNT_Y = 20; ///Шанс появления ягод на старте в процентах. ///При выставленном значении спавнится 3-5 ягод. ///Не беспокойтесь, что значение слишком низкое, как минимум одна ягода создаётся отдельно. public static final int INITIAL_SPAWN_CHANCE = 1;//% ///В нашем случае змея проходит одну клетку за один фрейм. ///Значение 5 мне показалось оптимальным, но вы можете экспериментировать. public static final int FPS = 5; ///Константы для создания окна, названия достаточно говорящие. public static final int SCREEN_WIDTH =CELLS_COUNT_X*CELL_SIZE; public static final int SCREEN_HEIGHT = CELLS_COUNT_Y*CELL_SIZE; public static final String SCREEN_NAME = "Tproger's Snake"; >
Enum Sprite , который отвечает за подгрузку текстур, полностью идентичен тому, что мы писали для Сапёра, за исключением того, что нам нужно только две текстуры — для змеи и для ягод. Вот код:
public enum Sprite < ///Файлы с именами circle и cherries должны лежать по адресу /// %папка проекта%/res/ в расширении .png BODY("circle"), CHERRIES("cherries"); private Texture texture; private Sprite(String texturename)< try < this.texture = TextureLoader.getTexture("PNG", new FileInputStream(new File("res/"+texturename+".png"))); >catch (IOException e) < e.printStackTrace(); >> public Texture getTexture() < return this.texture; >>
Механика игры
Самое время поговорить о том, как наша змея будет, собственно, перемещаться. Вам наверняка доводилось видеть вывески, вокруг которых по кругу бегают огоньки? Разумеется, сами лампочки в них не перемещаются, просто каждый тик последняя гаснет, а первая зажигается. Таким же образом будет перемещаться и наша змея.
Несложно подсчитать, что каждая лампочка должна гореть столько тиков, какова длина “змеи”. Значит, мы должны сообщить клетке, в которую попадает змея, что она должна гореть определённое количество секунд, а каждый тик уменьшать это число у каждой клетки с ненулевым таймером, и менять спрайт, если змея из клетки уже выползла (т.е. таймер стал равен нулю). В случае же необходимости удлинить цепочку, достаточно просто не уменьшать время “горения” клеток на каком-то тике. Именно поэтому метод update() у классов Cell и GUI принимает параметр — если он равен false , значит, змея что-то съела.
Пишем класс клетки
public class Cell < private int x; private int y; private int state;/* 0 ->ячейка пуста >0 -> в ячейке тело змеи, которое будет там ещё N фреймов Что-то необычное: -1: Ягоды */ ///Конструктор просто выставляет начальные значения координат и состояния public Cell (int x, int y, int state) < this.x=x; this.y=y; this.state=state; >///==== Ничем не примечательные геттеры и сеттеры public int getX() < return x; >public int getY() < return y; >public int getHeight() < return CELL_SIZE; >public int getWidth() < return CELL_SIZE; >public int getState() < return this.state; >public void setState(int state) < this.state = state; >///==== ///Метод обновления клетки. Уменьшаем время "горения", если это необходимо public void update(boolean have_to_decrease) < if (have_to_decrease && this.state >0) < this.state--; >> ///Ячейка "думает" как она должна выглядеть public Sprite getSprite() < if(this.state >0)< ///Если в ней тело змеи -- как змея return Sprite.BODY; >else if(this.state==0)< ///Если в ней нет ничего -- никак выглядеть и не должна return null; >else < ///Иначе проходимся свитчем по возможным объектам. ///Так как это демо -- я добавил только ягоды switch(this.state)< default: return Sprite.CHERRIES; >> > >
Добавляем геттер и сеттер для состояния клетки поля в GUI
getState(x,y) < return cells[x][y].getState(); >setState(x,y,state)
Пишу змейку в IntellijIdea на java. Подскажите, пожалуйста, почему может не работать ни repaint(), ни update(g), ни updateUI() [закрыт]
Вопросы с просьбами помочь с отладкой («почему этот код не работает?») должны включать желаемое поведение, конкретную проблему или ошибку и минимальный код для её воспроизведения прямо в вопросе. Вопросы без явного описания проблемы бесполезны для остальных посетителей. См. Как создать минимальный, самодостаточный и воспроизводимый пример.
Закрыт 2 года назад .
Змейка ползёт только если сворачивать и разворачивать окно ( А так стоит на месте. Может ли это быть связано с настройками самой IntelliJIdea, или проблема в моей программе? Вот код:
import javax.swing.*; import java.awt.*; import java.util.ArrayList; public class Snake extends JPanel implements Runnable < public static int length; public static ArrayListparts; public static Field field; public static Food food; public static int speedX=0; public static int speedY=1; //private Graphics g; private static Snake snake = null; public static final int TIME_DELTA = 500; public static Snake getSnake(int w, int h) < if(snake == null) snake = new Snake(w, h); return snake; >private Snake(int Width, int Heigth) < super(true); food = new Food(); field = Field.getField(); Point start = new Point((int)Width/2, (int)Heigth/2); //размеры поля, а не окна parts = new ArrayList<>(); parts.add(start); Point p1 = new Point((int)start.getX(), ((int)start.getY())-1); parts.add(p1); Point p2 = new Point((int)start.getX(), ((int)p1.getY())-1); parts.add(p2); length = 3; // paint(g); > public static void move() < for (Point i: parts) < i.y-=1*speedY; i.x-=1*speedX; >> public static void eat() < Point np = new Point ((int)parts.get(length).getX(),(int)parts.get(length).getY()-1 ); parts.add(np); ++length; food.respawn(); >public static boolean checkFood() < if(parts.get(parts.size()-1).getX() == food.x && parts.get(parts.size()-1).getY()==food.y) return true; else return false; >public static boolean checkHead() < for (int i=1; iif(parts.get(parts.size()-1).getX() = field.sizeX || parts.get(parts.size()-1).getY() = field.sizeY ) return false; return true; > @Override public void paint(Graphics g) < super.paint(g); for (Point i: parts) g.fillRect((int) i.getX() * 10, (int) i.getY() * 10, 8, 8); g.setColor(Color.RED); g.fillRect(food.x * 10, food.y * 10, 8, 8); g.setColor(Color.BLACK); >@Override public void run() < while (checkHead()) < move(); repaint(); if(checkFood()) eat(); try < Thread.sleep(TIME_DELTA); >catch (InterruptedException e) < e.printStackTrace(); >> > >
import javax.swing.*; import java.awt.*; public class Window extends JPanel < private Graphics g; private Snake snake; public Window() < super(true); snake = Snake.getSnake(50, 50); Thread snakeThread = new Thread(snake); snakeThread.start(); >@Override public void paint(Graphics g)
import java.awt.*; public class Field < public static int sizeX, sizeY; public static int[][] coordinates; public static Field field = null; public static Field getField() < if(field == null) field = new Field(500, 500); return field; >private Field(int x, int y) < sizeX=x/10; sizeY=y/10; coordinates = new int[sizeX][sizeY]; for (int i=0; i> > public static void changeField(int x, int y, boolean SetOrDel)// т - добавить, иначе убрать < if (SetOrDel) < coordinates[x][y]=1; >else < coordinates[x][y]=0; >>
import javax.swing.*; import java.awt.*; import java.util.Random; public class Food extends JPanel < public static int x; public static int y; private static Random random; public Food() < super(true); random = new Random(); x = random.nextInt(50); y = random.nextInt(50); >@Override public void paint(Graphics g) < super.paint(g); g.setColor(Color.RED); g.fillRect(x * 10, y * 10, 8, 8); g.setColor(Color.BLACK); >public void respawn() < x = random.nextInt(40); y = random.nextInt(40); repaint(); >>
Поменяла конструктор, ничего не изменилось(. Он же не срабатывает при сворачивании и разворачивании окна?
private Snake(int Width, int Heigth) < super(true); food = new Food(); field = Field.getField(); Point start = new Point((int)Width/2, (int)Heigth/2); //размеры поля, а не окна parts = new ArrayList<>(); parts.add(start); Point p1 = new Point((int)start.getX(), ((int)start.getY())-1); parts.add(p1); Point p2 = new Point((int)start.getX(), ((int)p1.getY())-1); parts.add(p2); length = 3; timer = new Timer(TIME_DELTA, null); timer.addActionListener(new ActionListener() < @Override public void actionPerformed(ActionEvent e) < if(checkHead() ) < move(); repaint(); if (checkFood()) eat(); >> >); timer.start(); >
Java/Игра змейка
Если не терпится написать что-нибудь интересное, например игру — вы находитесь на правильной странице! Данная игра не является законченным продуктом, в ней почти нет коментариев, зато она работает и в ней есть несколько ключевых элементов с помощью которых можно написать свою собственную игру. В общем, берем исходники и доделываем/переделываем игру сами!
Дизайн программы [ править ]
На графическом поле со стенами ползает змейка, которой игрок управляет стрелками, собирая апельсины.
Функциональность [ править ]
//**//Для начала в объекта actions установим голову змейки в центр поля. Потом создаем 2 массива в которых будут храниться координаты //**//сегментов змейки. //**// Действия которые выполняются при загрузке объекта onClipEvent (load) //**//Переменная в которой хранится количество сегментов тела змейки body_count = 0; //**//Переменная необходимая для паузы в движении змейки move_timer=getTimer(); //**//Переменная необходимая для паузы при создании бонусов bonus_timer=getTimer(); //**//Переменная которая указывает создан ли бонус или нет. bonus_exist = 0; //**//Устанавливаем голову змейки в центр игрового поля _root.snake_head._x = 290; _root.snake_head._y = 250; //**//Создаем массивы в которых будем хранить координаты тела змейки var cell_x:Array = new Array(); var cell_y:Array = new Array(); //**// Затем методом дубликации создадим начальный хвост для змейки, и вписываем координаты каждого сегмента в массивы. //**//Создание хвоста змейки циклом от 1 до 7 for (i=1; i7; i++) duplicateMovieClip(_root.snake_body, 'snake_body'+i, i); //**//Расстановка сегментов хвоста на поле, снизу головы змейки _root['snake_body'+i]._x = _root.snake_head._x; _root['snake_body'+i]._y = _root.snake_head._y+(i*20); //**//Вписываем в массив новые переменные cell_x[i+1] = _root['snake_body'+i]._x; cell_y[i+1] = _root['snake_body'+i]._y; //**//Записываем в переменной количество сегментов тела body_count++; >> //**//Затем добавим код, который будет управлять движением змейки, он очень прост, при нажатии на кнопку управления - голова змейки //**//меняет свое направление. //**//Блок управления движением змейки при нажатии кнопки - змейка меняет направление on (keyPress "") _root.snake_head._rotation = -90; > on (keyPress "") _root.snake_head._rotation = 90; > on (keyPress "") _root.snake_head._rotation = 0; > on (keyPress "") _root.snake_head._rotation = 180; > //**//Для движения змейки, появления бонусов, проверки столкновений, испольуем событие которое будет проверяться каждый кадр //**//мувика(onClipEvent (enterFrame)). Напишем код который будет дублировать мувик бонуса и размещать его в случайном месте на сцене, //**//перед дубликацией мувика проверяется тайме и переменная которая указывает создан бонус или нет, это нужно для того чтобы на //**//сцене размещался только 1 бонус. onClipEvent (enterFrame) //**//Создание бонусов. //**//Проверяем, если таймер меньше и переменная указывает что бонуса не создано - создаем бонус. if (getTimer()-bonus_timer>500 && !bonus_exist) //**//Дублируем клип, присваиваем ему имя duplicateMovieClip(_root.bonus, 'bonus_real', 1000); //**//Устанавливаем случайные координаты для дублированного бонуса _root.bonus_real._x = random(30)*20+10; _root.bonus_real._y = random(25)*20+10; //**//Ставим в переменной что бонус создан. bonus_exist = 1; > //**//Движение тела змейки по клеткам с проверкой таймера, для создания задержки и вписывание новых данных в массивы //**//Движение змейки //**//Проверяем таймер, для движения с задержкой. if (getTimer()-move_timer>170) //**//Обновление таймера движения. move_timer = getTimer(); //**//вписываем в массив координаты головы змейки cell_x[1] = _root.snake_head._x; cell_y[1] = _root.snake_head._y; //**//Движение головы змейки в клетку по направлению(_rotation) _root.snake_head._x += 20*Math.sin(_root.snake_head._rotation*(Math.PI/180)); _root.snake_head._y -= 20*Math.cos(_root.snake_head._rotation*(Math.PI/180)); //**//Запись циклом сегментов тела змейки в массивы for (i=1; ibody_count+1; i++) _root['snake_body'+i]._x = cell_x[i]; _root['snake_body'+i]._y = cell_y[i]; > //**//Передвижение циклом всех сегментов тела змейки в вышестоящую ячейку массива. for (i=1; ibody_count+1; i++) cell_x[i+1] = _root['snake_body'+i]._x; cell_y[i+1] = _root['snake_body'+i]._y; > //**//Проверку столкновения головы змейки с бонусом, осуществяется встроенной функцией Flash - hitTest(), если столкновение произошло //**//- дублируем для тела змейки новые сегменты и устанавливаем им позицию в хвосте змейки на место последнего сегмента, при //**//последующем движении змейки - они будут следовать за ней, и вписывать свои координаты в массивы. //**//Проверка столкновения головы змейки с бонусом методом HitTest`а и добавление новых сегментов к телу змейки if (_root.snake_head.hitTest(_root.bonus_real._x, _root.bonus_real._y)) trace('Bonus eat, you grow!'); removeMovieClip(_root.bonus_real); //**//Устанавливаем переменную, что бонус съеден и обновляем таймер для создания нового бонуса bonus_exist = 0; bonus_timer = getTimer(); //**//Добавляем циклом новые сегменты тела к змейке на последний сегмент тела и увеличиваем переменную отвечающую за длину тела. for (i=0; i4; i++) duplicateMovieClip(_root.snake_body, 'snake_body'+body_count, body_count); body_count++; _root['snake_body'+body_count]._x = _root['snake_body'+(body_count-1)]._x; _root['snake_body'+body_count]._y = _root['snake_body'+(body_count-1)]._y; cell_x[body_count] = _root['snake_body'+body_count]._x; cell_y[body_count] = _root['snake_body'+body_count]._y; > > //**//Проверка столкновения головы змейки с телом осуществляется циклом с функцией hitTest(), циклом проверяются все сегменты змейки //**//на столкновение с головой. Если столкновение произошло - функцией fscommand осуществляется закрытие мувика. //Проверка столкновения головы с сегментами тела HitTest`ом for (i=0; ibody_count+1; i++) //**//Если столкновение произошло - выводим сообщение и закрываем мувик. if (_root.snake_head.hitTest(_root['snake_body'+i]._x, _root['snake_body'+i]._y, 0)) trace('You eat your body. Cannibal !'); fscommand('quit', true); > > > >
Классы [ править ]
- SnakeApp — Окно игры. Окно игры содержит игру, слушает клавиатуру и пересылает команды
- SnakeGame — Главный класс игры. В сущности вся игра тут
- Snake — класс змейки. Змейка вынесена в отдельный класс, чтобы в будущем была возможность создания нескольких экземпляров одновременно
Отладка [ править ]
Так как программа сырая, вам предлагается ее улучшить и доделать. Вот неполный список вещей, которые нужно исправить:
- Нет проверок на самосъедание
- После аварии змейка может продолжить движение (уйти в бесконечность)
- Нет возможности перезапустить игру не закрывая окна
Исходники [ править ]
- SnakeApp.java — Окно игры
- SnakeGame.java — Главный класс игры
- Snake.java — класс змейки
- sn1.dat — текстовый файл с данными о стенках (поместите его в C:\ или измените путь в классе SnakeGame или в папке NetBeans scr. )
ZhekovIlya / KeyboardObserver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
package com . javarush . task . task23 . task2312 ; |
import javax . swing .*; |
import java . awt .*; |
import java . awt . event . FocusEvent ; |
import java . awt . event . FocusListener ; |
import java . awt . event . KeyEvent ; |
import java . awt . event . KeyListener ; |
import java . util . Queue ; |
import java . util . concurrent . ArrayBlockingQueue ; |
public class KeyboardObserver extends Thread |
private Queue < KeyEvent >keyEvents = new ArrayBlockingQueue < KeyEvent >( 100 ); |
private JFrame frame ; |
@ Override |
public void run () |
frame = new JFrame ( «KeyPress Tester» ); |
frame . setTitle ( «Transparent JFrame Demo» ); |
frame . setDefaultCloseOperation ( JFrame . EXIT_ON_CLOSE ); |
frame . setUndecorated ( true ); |
frame . setSize ( 400 , 400 ); |
frame . setExtendedState ( JFrame . MAXIMIZED_BOTH ); |
frame . setLayout ( new GridBagLayout ()); |
frame . setOpacity ( 0.0f ); |
frame . setVisible ( true ); |
frame . addFocusListener ( new FocusListener () |
@ Override |
public void focusGained ( FocusEvent e ) |
//do nothing |
> |
@ Override |
public void focusLost ( FocusEvent e ) |
System . exit ( 0 ); |
> |
>); |
frame . addKeyListener ( new KeyListener () |
public void keyTyped ( KeyEvent e ) |
//do nothing |
> |
public void keyReleased ( KeyEvent e ) |
//do nothing |
> |
public void keyPressed ( KeyEvent e ) |
keyEvents . add ( e ); |
> |
>); |
> |
public boolean hasKeyEvents () |
return ! keyEvents . isEmpty (); |
> |
public KeyEvent getEventFromTop () |
return keyEvents . poll (); |
> |
> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
package com . javarush . task . task23 . task2312 ; |
public class Mouse |
private int x ; |
private int y ; |
public Mouse ( int x , int y ) |
this . x = x ; |
this . y = y ; |
> |
public int getX () |
return x ; |
> |
public int getY () |
return y ; |
> |
> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
package com . javarush . task . task23 . task2312 ; |
import java . awt . event . KeyEvent ; |
import java . security . SecureRandom ; |
import java . util . ArrayList ; |
/** |
* Основной класс программы. |
*/ |
public class Room |
public static Room game ; |
private int width ; |
private int height ; |
private Snake snake ; |
private Mouse mouse ; |
private int initialDelay = 520 ; |
private int delayStep = 20 ; |
public Room ( int width , int height , Snake snake ) |
this . width = width ; |
this . height = height ; |
this . snake = snake ; |
game = this ; |
> |
public static void main ( String [] args ) |
game = new Room ( 20 , 20 , new Snake ( 10 , 10 )); |
game . snake . setDirection ( SnakeDirection . DOWN ); |
game . createMouse (); |
game . run (); |
> |
public Snake getSnake () |
return snake ; |
> |
public void setSnake ( Snake snake ) |
this . snake = snake ; |
> |
public Mouse getMouse () |
return mouse ; |
> |
public void setMouse ( Mouse mouse ) |
this . mouse = mouse ; |
> |
public int getWidth () |
return width ; |
> |
public void setWidth ( int width ) |
this . width = width ; |
> |
public int getHeight () |
return height ; |
> |
public void setHeight ( int height ) |
this . height = height ; |
> |
/** |
* Основной цикл программы. |
* Тут происходят все важные действия |
*/ |
public void run () |
//Создаем объект «наблюдатель за клавиатурой» и стартуем его. |
KeyboardObserver keyboardObserver = new KeyboardObserver (); |
keyboardObserver . start (); |
//пока змея жива |
while ( snake . isAlive ()) |
//»наблюдатель» содержит события о нажатии клавиш? |
if ( keyboardObserver . hasKeyEvents ()) |
KeyEvent event = keyboardObserver . getEventFromTop (); |
//Если равно символу ‘q’ — выйти из игры. |
if ( event . getKeyChar () == ‘q’ ) return ; |
//Если «стрелка влево» — сдвинуть фигурку влево |
if ( event . getKeyCode () == KeyEvent . VK_LEFT ) |
snake . setDirection ( SnakeDirection . LEFT ); |
//Если «стрелка вправо» — сдвинуть фигурку вправо |
else if ( event . getKeyCode () == KeyEvent . VK_RIGHT ) |
snake . setDirection ( SnakeDirection . RIGHT ); |
//Если «стрелка вверх» — сдвинуть фигурку вверх |
else if ( event . getKeyCode () == KeyEvent . VK_UP ) |
snake . setDirection ( SnakeDirection . UP ); |
//Если «стрелка вниз» — сдвинуть фигурку вниз |
else if ( event . getKeyCode () == KeyEvent . VK_DOWN ) |
snake . setDirection ( SnakeDirection . DOWN ); |
> |
snake . move (); //двигаем змею |
print (); //отображаем текущее состояние игры |
sleep (); //пауза между ходами |
> |
//Выводим сообщение «Game Over» |
System . out . println ( «Game Over!» ); |
> |
/** |
* Выводим на экран текущее состояние игры |
*/ |
public void print () |
//Создаем массив, куда будем «рисовать» текущее состояние игры |
int [][] matrix = new int [ height ][ width ]; |
//Рисуем все кусочки змеи |
ArrayList < SnakeSection >sections = new ArrayList < SnakeSection >( snake . getSections ()); |
for ( SnakeSection snakeSection : sections ) |
matrix [ snakeSection . getY ()][ snakeSection . getX ()] = 1 ; |
> |
//Рисуем голову змеи (4 — если змея мертвая) |
matrix [ snake . getY ()][ snake . getX ()] = snake . isAlive () ? 2 : 4 ; |
//Рисуем мышь |
matrix [ mouse . getY ()][ mouse . getX ()] = 3 ; |
//Выводим все это на экран |
String [] symbols = < " . " , " x " , " X " , "^_^" , "RIP" >; |
for ( int y = 0 ; y < height ; y ++) |
for ( int x = 0 ; x < width ; x ++) |
System . out . print ( symbols [ matrix [ y ][ x ]]); |
> |
System . out . println (); |
> |
System . out . println (); |
System . out . println (); |
System . out . println (); |
> |
/** |
* Метод вызывается, когда мышь съели |
*/ |
public void eatMouse () |
createMouse (); |
> |
/** |
* Создает новую мышь |
*/ |
public void createMouse () |
int x = ( int ) ( Math . random () * width ); |
int y = ( int ) ( Math . random () * height ); |
mouse = new Mouse ( x , y ); |
> |
/** |
* Программа делает паузу, длинна которой зависит от длинны змеи. |
*/ |
public void sleep () |
try |
int level = snake . getSections (). size (); |
int delay = level < 15 ? ( initialDelay - delayStep * level ) : 200 ; |
Thread . sleep ( delay ); |
> catch ( InterruptedException e ) |
> |
> |
> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
package com . javarush . task . task23 . task2312 ; |
import java . util . ArrayList ; |
/** |
* Класс змея |
*/ |
public class Snake |
//Направление движения змеи |
private SnakeDirection direction ; |
//Состояние — жива змея или нет. |
private boolean isAlive ; |
//Список кусочков змеи. |
private ArrayList < SnakeSection >sections ; |
public Snake ( int x , int y ) |
sections = new ArrayList < SnakeSection >(); |
sections . add ( new SnakeSection ( x , y )); |
isAlive = true ; |
> |
public boolean isAlive () |
return isAlive ; |
> |
public int getX () |
return sections . get ( 0 ). getX (); |
> |
public int getY () |
return sections . get ( 0 ). getY (); |
> |
public SnakeDirection getDirection () |
return direction ; |
> |
public void setDirection ( SnakeDirection direction ) |
this . direction = direction ; |
> |
public ArrayList < SnakeSection >getSections () |
return sections ; |
> |
/** |
* Метод перемещает змею на один ход. |
* Направление перемещения задано переменной direction. |
*/ |
public void move () |
if (! isAlive ) return ; |
if ( direction == SnakeDirection . UP ) |
move ( 0 , — 1 ); |
else if ( direction == SnakeDirection . RIGHT ) |
move ( 1 , 0 ); |
else if ( direction == SnakeDirection . DOWN ) |
move ( 0 , 1 ); |
else if ( direction == SnakeDirection . LEFT ) |
move (- 1 , 0 ); |
> |
/** |
* Метод перемещает змею в соседнюю клетку. |
* Координаты клетки заданы относительно текущей головы с помощью переменных (dx, dy). |
*/ |
private void move ( int dx , int dy ) |
//Создаем новую голову — новый «кусочек змеи». |
SnakeSection head = sections . get ( 0 ); |
head = new SnakeSection ( head . getX () + dx , head . getY () + dy ); |
//Проверяем — не вылезла ли голова за границу комнаты |
checkBorders ( head ); |
if (! isAlive ) return ; |
//Проверяем — не пересекает ли змея саму себя |
checkBody ( head ); |
if (! isAlive ) return ; |
//Проверяем — не съела ли змея мышь. |
Mouse mouse = Room . game . getMouse (); |
if ( head . getX () == mouse . getX () && head . getY () == mouse . getY ()) //съела |
sections . add ( 0 , head ); //Добавили новую голову |
Room . game . eatMouse (); //Хвот не удаляем, но создаем новую мышь. |
> else //просто движется |
sections . add ( 0 , head ); //добавили новую голову |
sections . remove ( sections . size () — 1 ); //удалили последний элемент с хвоста |
> |
> |
/** |
* Метод проверяет — находится ли новая голова в пределах комнаты |
*/ |
private void checkBorders ( SnakeSection head ) |
if (( head . getX () < 0 || head . getX () >= Room . game . getWidth ()) || head . getY () < 0 || head . getY () >= Room . game . getHeight ()) |
isAlive = false ; |
> |
> |
/** |
* Метод проверяет — не совпадает ли голова с каким-нибудь участком тела змеи. |
*/ |
private void checkBody ( SnakeSection head ) |
if ( sections . contains ( head )) |
isAlive = false ; |
> |
> |
> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
package com . javarush . task . task23 . task2312 ; |
public enum SnakeDirection |
UP , |
RIGHT , |
DOWN , |
LEFT |
> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
package com . javarush . task . task23 . task2312 ; |
public class SnakeSection |
private int x ; |
private int y ; |
public SnakeSection ( int x , int y ) |
this . x = x ; |
this . y = y ; |
> |
public int getX () |
return x ; |
> |
public int getY () |
return y ; |
> |
@ Override |
public boolean equals ( Object o ) |
if ( this == o ) return true ; |
if ( o == null || getClass () != o . getClass ()) return false ; |
SnakeSection that = ( SnakeSection ) o ; |
if ( x != that . x ) return false ; |
if ( y != that . y ) return false ; |
return true ; |
> |
@ Override |
public int hashCode () |
int result = x ; |
result = 31 * result + y ; |
return result ; |
> |
> |