Перейти к содержимому

Как написать змейку на java

  • автор:

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 — класс змейки. Змейка вынесена в отдельный класс, чтобы в будущем была возможность создания нескольких экземпляров одновременно

Отладка править

Так как программа сырая, вам предлагается ее улучшить и доделать. Вот неполный список вещей, которые нужно исправить:

  1. Нет проверок на самосъедание
  2. После аварии змейка может продолжить движение (уйти в бесконечность)
  3. Нет возможности перезапустить игру не закрывая окна

Исходники править

  • SnakeApp.java — Окно игры
  • SnakeGame.java — Главный класс игры
  • Snake.java — класс змейки
  • sn1.dat — текстовый файл с данными о стенках (поместите его в C:\ или измените путь в классе SnakeGame или в папке NetBeans scr. )

Пишу змейку в 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 за 15 минут

Обложка поста Как написать свою змейку на 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)

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

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

Как написать змейку на java

в розділі ігри в мене була помилка що java fx не встановлена можливо хтось допоможе, хотілось б вирішити їх а то можу запускати гру тільки коли вона повністю готова

Роман Уровень 39
20 ноября 2022

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

Kurama Уровень 50
2 ноября 2022
Я уже давно написал её в разделе «игры» и даже добавил свою фишку в код не по заданию
Михаил Уровень 32
22 октября 2022
В результате решения этой задачи я понял и принял свою совершенную бесполезность.
Mykhailo_Trofimov Уровень 42
7 октября 2022

Меня одного напрягает замена моего кода на код от javaRush. Зачем так делать? Мой код прошел тесты? — прошел. Значит он валидный? — валидный. Но на наш код им пользуйся. И класс KeyboardObserver тоже можно было дать реализовать как задание. Ну про многопоточность ниже писали, просто поддержу!

Сообщество

JavaRush — это интерактивный онлайн-курс по изучению Java-программирования c нуля. Он содержит 1200 практических задач с проверкой решения в один клик, необходимый минимум теории по основам Java и мотивирующие фишки, которые помогут пройти курс до конца: игры, опросы, интересные проекты и статьи об эффективном обучении и карьере Java‑девелопера.

Подписывайтесь
Язык интерфейса
«Программистами не рождаются» © 2023 JavaRush
Скачивайте наши приложения
«Программистами не рождаются» © 2023 JavaRush

Этот веб-сайт использует данные cookie, чтобы настроить персонально под вас работу сервиса. Используя веб-сайт, вы даете согласие на применение данных cookie. Больше подробностей — в нашем Пользовательском соглашении.

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

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