Откроемсреду разработки Eclipseдля Java и создадим новый проект Java. Для этого в меню выберем: File-New-Java Project (см. рис. 7)
Рис. 7
Укажем название проекта: zmeika (см. рис. 8) и нажмем кнопку Finish(Готово).
Рис. 8
В списке проектов добавится новый проект zmeika. Добавим к нему новый класс.Для этого нажмем на имя проекта левой клавишей мыши (выделим). Далее нажмем правую клавишу мыши, откроется дополнительное окно. Выберем New-Class(см. рис. 9):
Рис. 9
Это будет главный класс нашего проекта,в нем будет находиться метод main(). Укажем название класса: zmeika и поставим галочку public static void main(см.рис. 10):
Рис. 10
Далее нажимаем кнопкуFinish (Готово). В результате появится программный код нового класса, содержащий метод main() (см. рис. 11):
Рис. 11
При разработке игрыЗмейка мы не будем использовать визуальный конструктор, потому что интерфейс довольно простой. Элементы интерфейса - кнопки и надпись мы создадим программно. На первом занятии мы выяснили, что программирование игры Змейкаможно свести к программированию двумерного массива. Таймер отрисовки будет отображать двумерный массив, перенося его значения на игровое поле. Все объекты игрового поля будут представлены в массиве в виде чисел. Создание игры можно разбить на две основные части –это различные действия с массивом для перемещения змейки и графическое отображение игрового поля.
Для операций с массивом создадим еще один класс. Добавим к проекту еще один класс с названием: game (см.рис. 12), галочку public static void main() на этот раз ставить не нужно! (см. рис.12).
Рис. 12
В результате в нашемпроекте будет два файла (см.рис. 13, 14):
Рис. 13
Рис. 14
В файле game.javaбудет реализована вся логика игры –это операции с двумерным массивом. В файле zmeika.javaбудет реализована вся графика, построение интерфейса, обработка событий, отрисовка и настройка таймеров.
Начнем с класса game. Для первого уровня сложности достаточно выводить по центру только голову змейки, которая не перемещается и объект для поедания,появляющийся в случайном месте.
Сначала добавим свойства класса, которые нам понадобятся при работе с массивом. Нам будет достаточно только двумерного массива. Итак, начнем писать программный код в файле game.java:
public class game
{
// Двумерный массив для хранения игрового поля
public int[][] mas;
}
Объявление int[][] mas; означает двумерный массив целых чисел. Свойство класса –это переменная класса. Данную переменную класса мы сделали public –открытой. Это необходимо для доступности массива из другого класса. К данным массива мы будем обращаться для отрисовки данных игрового поля. Добавим конструктор класса:
public class game
{
// Двухмерный массив для хранения игрового поля
public int[][] mas;
// Конструктор класса
public game()
{
//Создаем новый массив 30x30
mas = new int[30][30];
}
}
Конструктор класса автоматически вызывается при создании объекта на основании класса. Имя конструктора должно совпадать с именем класса.
В конструкторе мы создаем двумерный массив размером 30x30,тридцать строк и тридцать элементов в каждой строке. Именно такие размеры имеет игровое поле для змейки. Когда происходит объявление массива, массива еще нет. Он создается при помощи команды new. При объявлении массива мы лишь сообщаем, что тип данных–двумерный массив.
Далее нам понадобится метод для генерации положения объекта для поедания змейкой. Внутри (под конструктором класса) класса, добавим метод make_new():
// Генерация нового объекта в случайном месте
private void make_new()
{
//Глухой (бесконечный цикл)
while(true)
{
// Получаем случайные значения x,y от 0 до 29
int x = (int)(Math.random()*30);
int y = (int)(Math.random()*30);
// Если в этом месте массива нулевое значение
// то помещаем туда объект для поедания змейкой
// и прерываем цикл
if (mas[y][x]==0)
{
mas[y][x] = -1;
// Прерываем цикл
break;
}
}
}
При рассмотрении алгоритма написания игры мы говорили, что свободные клетки игрового поля в массиве будут иметь нулевое значение, объект для поедания будет обозначен минус единицей:-1, голова змейки обозначена единицей: 1, а туловище змейки и -числа от двух и выше.
Это значит, что для размещения "объекта поедания" нужно найти случайно выбранную свободную ячейку.Алгоритм поиска заключается в следующем:мы ищем случайным образом ячейку до тех пор, пока не найдем свободную. Для такого поиска можно использовать бесконечный цикл:
//Глухой (бесконечный цикл)
while(true)
{
}
Цикл будет прерван в тот момент, когда будет найдена ячейка с нулевым значением:
// Если в этом месте массива нулевое значение
// то помещаем туда объект для поедания змейкой
// и прерываем цикл
if (mas[y][x]==0)
{
mas[y][x] = -1;
// Прерываем цикл
break;
}
Перед выходом из цикла записываем в эту ячейку минус единицу -"объект для поедания". Для поиска случайной ячейки на игровом поле используем следующую конструкцию:
// Получаем случайные значения x,y от 0 до 29
int x = (int)(Math.random()*30);
int y = (int)(Math.random()*30);
Для получения искомой ячейки нужно выбрать случайную строку и случайный номер элемента в строке. И эти значения (номер строки и номер элемента в строке) должны лежать от 0 до 29включительно. Нумерация в массиве начинается с нуля. Последний метод, который нам понадобится в этом классе – это метод для старта(запуска) игры. Под методом make_new()добавим методstart():
// Запуск игры (Старт)
public void start()
{
//Заполняем весь массив нулями
for (int i = 0; i < 30; i++) {
for (int j = 0; j < 30; j++) {
mas[i][j] = 0;
}
}
//Размещаем голову змейки по центру поля
mas[15][15] = 1;
//Формируем первый объект для поедания змейкой
make_new();
}
Этот метод содержит действия при начале игры. Сначала обнуляем весь массив при помощи двух вложенных циклов –это равноценно полному очищению игрового поля.Далее по центру игрового поля размещаем голову змейки (помещаем единицу):
//Размещаем голову змейки по центру поля
mas[15][15] = 1;
Теперь генерируем случайным образом "объект для поедания",вызывая метод make_new(), созданный на предыдущем шаге.Полностью программный код класса game для первого уровня сложности выглядит так:
public class game
{
// Двухмерный массив для хранения игрового поля
public int[][] mas;
// Конструктор класса
public game()
{
//Создаем новый массив 30x30
mas = new int[30][30];
}
// Генерация нового объекта в случайном месте
private void make_new()
{
//Глухой (бесконечный цикл)
while(true)
{
// Получаем случайные значения x,y от 0 до 29
int x = (int)(Math.random()*30);
int y = (int)(Math.random()*30);
// Если в этом месте массива нулевое значение
// то помещаем туда объект для поедания змейкой
// и прерываем цикл
if (mas[y][x]==0)
{
mas[y][x] = -1;
break;
}
}
}
// Запуск игры (Старт)
public void start()
{
//Заполняем весь массив нулями
for (int i = 0; i < 30; i++) {
for (int j = 0; j < 30; j++) {
mas[i][j] = 0;
}
}
//Размещаем голову змейки по центру поля
mas[15][15] = 1;
//Формируем первый объект для поедания змейкой
make_new();
}
}
Перейдем в другой файл нашего проекта: zmeika.java.В этом файле будет реализована остальная часть игры. В самом верху добавим необходимые библиотеки:
// Для обработки событий
import java.awt.event.*;
// Для работы с окнами
import javax.swing.*;
// Для работы с графикой
import java.awt.*;
// Для работы с изображениями
import javax.imageio.*;
// Для работы с файлами
import java.io.*;
Все эти библиотеки нам уже знакомы по игре "Новогодний дождь".Внутри метода main(), который является точкой входа в программу, добавим создание объекта "окно"с игровым полем:
// Главный класс программы
public class zmeika
{
// Метод запуска приложения
public static void main(String[] args)
{
// Создание объекта окна игрового поля
myFrame okno = new myFrame();
}
}
Сам класс myFrame будет создан на следующем шаге и будет располагаться под классом zmeika:
// Класс окна игрового поля
class myFrame extends JFrame
{
// Конструктор класса
public myFrame()
{
//Создание объекта панели и подключения ее к окну
myPanel pan = new myPanel();
Container cont = getContentPane();
cont.add(pan);
//Заголовок окна
setTitle("Игра \"Змейка\"");
//Границы окна: расположение и размеры
setBounds(0, 0, 800, 650);
//Операция при закрытии окна - завершение приложения
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Запрет изменения размеров окна
setResizable(false);
//Отображение (показ) окна
setVisible(true);
}
}
Класс myFrameнаследуется от класса JFrame –класса окон. Внутри класса myFrameнаходится конструктор класса, который задаёт все характеристики окна. Все эти действия мы выполняли при создании игры "Новогодний дождь". Отдельного внимания заслуживает строка:
// Запрет изменения размеров окна
setResizable(false);
Она запрещает пользователю изменять размеры окна по ширине и высоте.
При задании заголовка окна слово "Змейка" находится внутри двойных кавычек, поэтому перед кавычками стоит наклонная черта \.:
//Заголовок окна
setTitle("Игра \"Змейка\"");
Следующие строки подключают панель к окну:
// Создание объекта панели и подключения ее к окну
myPanel pan = new myPanel();
Container cont = getContentPane();
cont.add(pan);
Класс myPanelбудет располагаться под классом myFrame. Этот класс реализует игровое поле и будет самым большим по объему программного кода.Создадим класс myPanelи добавим свойства (переменные)класса:
// Класс панели игрового поля
class myPanel extends JPanel
{
// Переменная для реализации логики игры
private game myGame;
// Таймер отрисовки
private Timer tmDraw;
// Изображения, используемые в игре
private Image fon,telo,golova,ob,endg;
// Надпись для количества очков
private JLabel lb;
// Две кнопки
private JButton btn1,btn2;
}
Среди свойств представлено все, что понадобится нам на игровом поле:
· переменная типа game(класс, созданный в самом начале для реализации двумерного массива),
Затем создаем объект на основании класса game и производим запуск игры:
//Создаем объект новой игры
myGame = new game();
myGame.start();
В этот момент создается двумерный массив 30x30и в него вписываются значения при запуске игры. После этого настраиваем таймер для отрисовки:
// Создаем, настраиваем и запускаем таймер
// для отрисовки игрового поля
tmDraw = new Timer(20,new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// Вызываем перерисовку -paintComponent()
repaint();
}
});
tmDraw.start();
Таймер вызывает методrepaint() и перерисовывает игровое поле. Скорость срабатывания таймера –20 миллисекунд (50 раз в секунду). Теперь приступаем к добавлению элементов интерфейса и включаем возможность произвольного размещения:
setLayout(null);
Добавляем текстовую надпись, которая показывает счет игры:
lb = new JLabel("Счет: 0");
lb.setForeground(Color.WHITE);
lb.setFont(new Font("serif",0,30));
lb.setBounds(630, 200, 150, 50);
add(lb);
Указываем цвет, шрифт,расположение и в конце добавляем ее к панели!
Затем добавляем кнопкуНовая игра:
btn1 = new JButton();
btn1.setText("Новая игра");
btn1.setForeground(Color.BLUE);
btn1.setFont(new Font("serif",0,20));
btn1.setBounds(630, 30, 150, 50);
btn1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
myGame.start();
}
});
add(btn1);
К кнопке подключается обработчик события при нажатии на кнопку.При нажатии вызывается метод: myGame.start(), который сбрасывает значения в массиве на начальные, и игра перезапускается. На последнем шаге в конструкторе добавляется вторая кнопка - Выход:
btn2 = new JButton();
btn2.setText("Выход");
btn2.setForeground(Color.RED);
btn2.setFont(new Font("serif",0,20));
btn2.setBounds(630, 100, 150, 50);
btn2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
System.exit(0);
}
});
add(btn2);
В обработчике события при нажатии на эту кнопку вызываетсяSystem.exit(0), что приводит к завершению работы приложения.Осталось добавить последний метод к классуmyPanel. Для этого ниже конструктора класса записываем следующий код:
// Метод отрисовки
public void paintComponent(Graphics gr)
{
super.paintComponent(gr);
//Отрисовка фона
gr.drawImage(fon,0,0,800,650,null);
//Отрисовка игрового поля на основании массива
for (int i = 0; i < 30; i++) {
for (int j = 0; j < 30; j++) {
if (myGame.mas[i][j]!=0)
{
if (myGame.mas[i][j]==1)
{
gr.drawImage(golova,10+j*20, 10+i*20,20,20,null);
}
else if (myGame.mas[i][j]==-1)
{
gr.drawImage(ob,10+j*20, 10+i*20,20,20,null);
}
}
}
}
//Отрисовка сетки игрового поля из синих линий
gr.setColor(Color.BLUE);
for (int i = 0; i <= 30; i++)
{
gr.drawLine(10+i*20, 10, 10+i*20, 610);
gr.drawLine(10, 10+i*20, 610, 10+i*20);
}
}
Метод paintComponent()отвечает за рисование всей графики. Этот метод мы многократно использовали ранее. В первую очередь рисуется фон игрового поля. Далее идут два вложенных цикла, которые перебирают все элементы двухмерного массива. Если значение нулевое –ячейка остается пустой, если значение равно-1, то выводится"объект для поедания", если значение равно 1,то выводится голова змейки.
gr.drawImage(golova,10+j*20, 10+i*20,20,20,null);
Метод drawImage() выводит изображение. Первый параметр –это переменная, в которую загружена картинка, второй и третий параметр –это координаты верхней левой угловой точки картинки, четвертый и пятый параметр – это ширина и высота картинки, последний параметр –пустое значение. Обратите внимание, что второй и третий параметр представляют из собой формулу. В эту формулу подставляются значения переменных i, j.
Чтобы понять, как работает подобная формула –можно взять лист бумаги и самому выполнить алгоритм пошагово, подставляя значения переменныхi,j. Тогда можно увидеть значения, которые увеличиваются с шагом в 20 пикселей –это размер одной клетки. На последнем шаге в методе рисуется сетка из линий:
//Отрисовка сетки игрового поля из синих линий
gr.setColor(Color.BLUE);
for (int i = 0; i <= 30; i++)
{
gr.drawLine(10+i*20, 10, 10+i*20, 610);
gr.drawLine(10, 10+i*20, 610, 10+i*20);
}
Цикл рисует вертикальные и горизонтальные линии. В методе drawLine()четыре параметра –первые два –это x,y начальной точки линии, вторые два–это x,yконечной точки линии. Цикл можно пройти пошагово и подставить значения переменной i:
Рис. 15
Первый шаг цикла, i=0:
gr.drawLine(10, 10, 10, 610);
gr.drawLine(10, 10, 610, 10);
Второй шаг цикла, i=1:
gr.drawLine(30, 10, 30, 610);
gr.drawLine(10, 30, 610, 30);
Третий шаг цикла, i=2:
gr.drawLine(50, 10, 50, 610);
gr.drawLine(10, 50, 610, 50);
Можно увидеть закономерность –первая линия сдвигается слева направо, а вторая линия сдвигается сверху вниз. Сдвиг происходит с шагом 20 пикселей.Рассмотренные три шага цикла нарисуют линии (см. рис. 15). На этом написание программного кода в файле zmeika.java закончено. Полностью программный код этого файла представлен ниже:
// Для обработки событий
import java.awt.event.*;
// Для работы с окнами
import javax.swing.*;
// Для работы с графикой
import java.awt.*;
// Для работы с изображениями
import javax.imageio.*;
// Для работы с файлами
import java.io.*;
// Главный класс программы
public class zmeika
{
// Метод запуска приложения
public static void main(String[] args)
{
//Создание объекта окна игрового поля
myFrame okno = new myFrame();
}
}
// Класс окна игрового поля
class myFrame extends JFrame
{
// Конструктор класса
public myFrame()
{
//Создание объекта панели и подключения ее к окну
myPanel pan = new myPanel();
Container cont = getContentPane();
cont.add(pan);
//Заголовок окна
setTitle("Игра \"Змейка\"");
//Границы окна: расположение и размеры
setBounds(0, 0, 800, 650);
//Операция при закрытии окна - завершение приложения