Реализация второго уровня сложности игры Змейка(завершение)

Все необходимые доработки в классе gameдля второго уровня сложности уже выполнены. В этом классе добавлены новые возможности, определяющие поведение змейки. Все это относится к логике игры.Теперь перейдем во второй файл нашего проекта zmeika.java.

Классы zmeikaи myFrame больше дорабатывать не нужно, мы их описали полностью! Они остаются без изменений для любого варианта игры. Все необходимые библиотеки также добавлены! Все это находится в самой верхней части файла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);

//Операция при закрытии окна - завершение приложения

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Запрет изменения размеров окна

setResizable(false);

//Отображение (показ) окна

setVisible(true);

}

}

Все изменения, которые мы сейчас внесем, затронут класс myPanel –класс панели окна, на которой расположено игровое поле. Начнем со свойств (переменных) класса. Нам понадобится еще один таймер, который будет изменять логику игры, а другими словами,изменять двумерный массив. Этот таймер будет вызывать метод для перемещения головы змейки. Назовем переменную tmUpdate:


// Переменная для реализации логики игры

private game myGame;

// Два таймера: отрисовки и изменения логики игры

private Timer tmDraw, tmUpdate;

// Изображения, используемые в игре

private Image fon,telo,golova,ob,endg;

// Надпись для количества очков

private JLabel lb;

// Двекнопки

private JButton btn1,btn2;

Сразу под блоком свойств(переменных) класса добавим класс для обработки событий от клавиатуры. Этот класс был использован в игре "Новогодний дождь":

// Класс для обработки событий от клавиатуры

private class myKey implements KeyListener

{

//Методпринажатиинаклавишу

public void keyPressed(KeyEvent e)

{

// Получениекоданажатойклавиши

int key = e.getKeyCode();

// Если нажатие одной из четырех стрелочек, то

// изменение направления змейки

if (key==KeyEvent.VK_LEFT) myGame.napr = 0;

else if (key==KeyEvent.VK_UP) myGame.napr = 1;

else if (key==KeyEvent.VK_RIGHT) myGame.napr = 2;

else if (key==KeyEvent.VK_DOWN) myGame.napr = 3;

}

public void keyReleased(KeyEvent e) {}

public void keyTyped(KeyEvent e) {}

}

При разработке программ невозможно запомнить названия всех классов и методов. И нет смысла ставить себе такую цель! Если программист использует разные языки программирования и много различных библиотек, то держать в голове все –это сложная задача.При регулярном написании программного кода, названия некоторых классов и методов запоминаются автоматически. Также помогает подсветка кода со списком подходящих наименований. Но переключившись на длительное время на другой язык программирования или другое направление разработки, программист забывает часть наименований. Для решения этого вопроса существует простой способ–база рецептов программирования. Допустим,программист познакомился с методикой обработки событий от клавиатуры на языкеJava при разработке для настольных компьютеров.Этот фрагмент программного кода можно сохранить, а потом использовать при разработке какого-либо проекта, внося при необходимости изменения.

Описанный выше класс для обработки нажатий клавиш уже использовался при создании игры"Новогодний дождь". Нужно лишь взять его и вставить в наш программный код.

Код нажатой клавиши попадает в переменнуюkey:

// Получение кода нажатой клавиши

int key = e.getKeyCode();

Нас интересуют клавиши Реализация второго уровня сложности игры Змейка(завершение) - student2.ru ("вправо"), Реализация второго уровня сложности игры Змейка(завершение) - student2.ru ("влево"), Реализация второго уровня сложности игры Змейка(завершение) - student2.ru ("вверх"), Реализация второго уровня сложности игры Змейка(завершение) - student2.ru ("вниз"),

Условная конструкция определяет - какое именно направление было выбрано пользователем:

// Если нажатие одной из четырех стрелочек,то

// изменение направления змейки

if (key==KeyEvent.VK_LEFT) myGame.napr = 0;

else if (key==KeyEvent.VK_UP) myGame.napr = 1;

else if (key==KeyEvent.VK_RIGHT) myGame.napr = 2;

else if (key==KeyEvent.VK_DOWN) myGame.napr = 3;

В зависимости от направления в переменную napr, которая находится в классе game,передается необходимое значение:0,1,2,3. Для удобства анализа кодов клавиш в языкеJavaимеется удобное перечисление KeyEvent.

после его названия через оператор "." ("точка"), можно выбрать наименование нужной клавиши. Наименования клавиш начинаются с двух букв VK.

Примеры:

VK_LEFT–клавиша Реализация второго уровня сложности игры Змейка(завершение) - student2.ru ("влево"), VK_DOWN–клавиша Реализация второго уровня сложности игры Змейка(завершение) - student2.ru ("вниз"), VK_3–цифра 3,VK_A–буква A, VK_ALT–клавиша<Alt>.

Таким образом, мы связываем нажатия нужных нам клавиш со значениями переменной napr в классе game. Созданный класс–это лишь шаблон, описание. Пока мы не воспользуемся классом –его содержимое не будет работать. Поэтому в самом начале конструктора класса myPanel добавим строчки:

// Конструктор класса

public myPanel()

{

// Подключение обработчика события для клавиатуры к панели

this.addKeyListener(new myKey());

// Делаем панель в фокусе -для приема событий от клавиатуры

this.setFocusable(true);

this –это обращение к текущему объекту, к панели окна, которое будет принимать события от клавиатуры.Метод addKeyListener() добавляет к панели обработчик события клавиатуры. Внутри круглых скобок метода создается объект на основании класса, созданного на предыдущем шаге:new myKey().

// Делаем панель в фокусе - для приема событий от клавиатуры

this.setFocusable(true);

Следующая строка делает панель в фокусе. Это нужно для приема панелью событий от клавиатуры.

Внутри метода paintComponent()для второго уровня сложности ничего дорабатывать не придется. Этот метод выполняет отрисовку игрового поля. Он отрисовывает его на основании данных двумерного массива. Если значение массива, соответствующее голове змейки будет перемещаться в самом массиве,то метод paintComponent() будет выводить ее уже в новом месте на игровом поле. Мы добавили переменную класса tmUpdate –это таймер для изменения логики игры. В конструкторе класса myPanelрядом с настройкой первого таймера tmDraw (выше или ниже)добавим код для таймера tmUpdate:

//Создаем, настраиваем и запускаем таймер для изменения

//логикиигры

tmUpdate = new Timer(100,new ActionListener() {

@Override

public void actionPerformed(ActionEvent arg0) {

// Перемещаем голову змейки

myGame.peremGolova();

// Выводим информацию о количестве очков

lb.setText("Счет: "+myGame.kol);

}

});

tmUpdate.start();

Этот участок программного кода описывает настройку и запуск таймера tmUpdate. Таймер будет срабатывать через 100миллисекунд, 10 раз в секунду. Он будет 10 раз в секунду вызывать метод peremGolova(), который изменяет положение головы змейки в массиве. Этот метод находится в классеgame.

// Перемещаем голову змейки

myGame.peremGolova();

Кроме этого таймер будет постоянно обновлять счет игры:

//Выводим информацию о количестве очков

lb.setText("Счет: "+myGame.kol);

После слова "Счет: "будет выводиться текущее значение переменнойkol, которая находится в классе game. При поедании объекта змейкой–значение переменной kolбудет увеличиваться на 10.

Сохраним наш проект и запустим игру(см. рис. 1).

Реализация второго уровня сложности игры Змейка(завершение) - student2.ru

Рис. 1

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

Но есть еще одна проблема, которую предстоит решить. Если в процессе игры нажать кнопку Новая игра, то счет обнулится –и это правильно! (см. рис.2)

Реализация второго уровня сложности игры Змейка(завершение) - student2.ru

Рис. 2

Но проблема в том, что после начала новой игры пропадет возможность управлением головой змейки.Она не будет реагировать на нажатие клавиш!Это связано с тем, что при нажатии на кнопку Новая игра она получает фокус, при этом панель теряет фокус. После этого все события от клавиатуры принимаются кнопкой,но наш обработчик события подключен именно к панели и не получает информацию о событиях кнопки. Поэтому после нажатия на кнопку нужно снова передавать фокус панели.

Добавим к классу myPanelеще одну переменную класса:

// Ссылканапанель

private myPanel pan;

В эту переменную мы поместим ссылку на панель с игровым полем.Обратите внимание, что тип переменной–myPanel. В самом верху конструктора класса myPanelдобавим строку:

// Конструктор класса

public myPanel()

{

// Помещаем ссылку на саму панель в переменную

pan = this;

Переменная pan будет ссылаться на панель. Теперь через эту переменную мы сможем обратиться к панели в любом месте программного кода класса myPanel. Последнее, что нужно сделать –это добавить три строки в обработчик события - нажатие на кнопкуНовая игра:

btn1.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent arg0) {

// Запуск игры

myGame.start();

// Забираем фокус у кнопки Новая игра

btn1.setFocusable(false);

// Забираем фокус у кнопки Выход

btn2.setFocusable(false);

// Отдаем фокус панели

pan.setFocusable(true);

}

});

После перезапуска игры мы забираем фокус у двух кнопок и передаем фокус панели. Если мы запустим игру снова,то увидим, что после перезапуска игры– все работает правильно! Создание игры Змейка второго уровня сложности закончено. Полный программный код файла 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);

//Операция при закрытии окна - завершение приложения

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Запрет изменения размеров окна

setResizable(false);

//Отображение (показ) окна

setVisible(true);

}

}

// Класс панели игрового поля

class myPanel extends JPanel

{

// Переменная для реализации логики игры

private game myGame;

// Два таймера: отрисовки и изменения логики игры

private Timer tmDraw, tmUpdate;

// Изображения, используемые в игре

private Image fon,telo,golova,ob,endg;

// Надпись для количества очков

private JLabel lb;

// Двекнопки

private JButton btn1,btn2;

// Ссылканапанель

private myPanel pan;

// Класс для обработки событий от клавиатуры

private class myKey implements KeyListener

{

//Методпринажатиинаклавишу

public void keyPressed(KeyEvent e)

{

// Получениекоданажатойклавиши

int key = e.getKeyCode();

// Если нажатие одной из четырех стрелочек, то

// изменение направления змейки

if (key==KeyEvent.VK_LEFT) myGame.napr = 0;

else if (key==KeyEvent.VK_UP) myGame.napr = 1;

else if (key==KeyEvent.VK_RIGHT) myGame.napr = 2;

else if (key==KeyEvent.VK_DOWN) myGame.napr = 3;

}

public void keyReleased(KeyEvent e) {}

public void keyTyped(KeyEvent e) {}

}

// Конструктор класса

public myPanel()

{

// Помещаем ссылку на саму панель в переменную

pan = this;

//Подключение обработчика события для клавиатуры к панели

this.addKeyListener(new myKey());

// Делаем панель в фокусе - для приема событий от клавиатуры

this.setFocusable(true);

//Попытка загрузки всех изображений для игры

try

{

fon = ImageIO.read(new File("c:\\fon.png"));

telo = ImageIO.read(new File("c:\\telo.png"));

golova = ImageIO.read(new File("c:\\golova.png"));

ob = ImageIO.read(new File("c:\\ob.png"));

endg = ImageIO.read(new File("c:\\endg.png"));

}

catch (Exception ex) {}

//Создаем объект новой игры

myGame = new game();

myGame.start();

//Создаем, настраиваем и запускаем таймер для

//отрисовкиигровогополя

tmDraw = new Timer(20,new ActionListener() {

@Override

public void actionPerformed(ActionEvent arg0) {

// Вызываемперерисовку -paintComponent()

repaint();

}

});

tmDraw.start();

//Создаем, настраиваем и запускаем таймер

// для изменения логики игры

tmUpdate = new Timer(100,new ActionListener() {

@Override

public void actionPerformed(ActionEvent arg0) {

// Перемещаем голову змейки

myGame.peremGolova();

// Выводим информацию о количестве очков

lb.setText("Счет: "+myGame.kol);

}

});

tmUpdate.start();

//Включаем возможность произвольного размещения

//элементов интерфейса на панели

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();

// Забираем фокус у кнопки Новая игры

btn1.setFocusable(false);

// Забираем фокус у кнопки Выход

btn2.setFocusable(false);

// Отдаем фокус панели

pan.setFocusable(true);

}

});

add(btn1);

//Создаем кнопку Выход

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);

}

// Метод отрисовки

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);

}

}

}

Наши рекомендации