Главная » Java » Заканчиваем игру Пинг-Понг

0

Теперь,  после краткого введения в потоки мы готовы поменять классы нашей   игры  в   пинг-­?понг.   Давайте   начнем   с   класса PingPongGreenTable.  Нам не надо отображать белую точку по клику мыши –  это  было  просто  учебное  упражнение  для  отображения координат   указателя   мыши.   Поэтому   мы  удалим   объявление переменной  point и строки, которые рисуют белую точку из  метода paintComponent().   Также, в конструкторе больше не нужен MouseListener, так как он только показывает координаты точки.С  другой стороны, этот класс должен реагировать на некоторые кнопкиклавиатуры  (N – для начала новой игры, S – для  подачи  мяча и Q – длявыхода  из игры). В этом нам поможет метод addKeyListener().Для того, чтобы  сделать наш код более инкапсулированным, я переместил  вызовы repaint()  из  класса  PingPongGameEngine  в класс   PingPongGreenTable.    Теперь,    когда    понадобится, PingPongGreenTable будет перерисовывать себя сам. Также,  я добавил методы для изменения положения мяча, ракеткикомпьютера  и для отображения сообщений. package screens;

import javax.swing.JPanel; import javax.swing.JFrame; import javax.swing.BoxLayout; import javax.swing.JLabel;

import javax.swing.WindowConstants;

import java.awt.Dimension; import java.awt.Container; import java.awt.Graphics; import java.awt.Color;

import engine.PingPongGameEngine;

/**

*Этот класс рисует зеленый стол для пинг-понга,

шар, ракетки, отображает счет

*/

public class PingPongGreenTable extends JPanel

implements GameConstants{

private JLabel label;

private int computerRacket_Y = COMPUTER_RACKET_Y_START;

private int kidRacket_Y = KID_RACKET_Y_START;

private int ballX = BALL_START_X;

private int ballY = BALL_START_Y;

Dimension preferredSize = new

Dimension(TABLE_WIDTH,TABLE_HEIGHT);

// Устанавливаем размеры окна.Вызывается виртуальной машиной

public Dimension getPreferredSize() {

return preferredSize;

}

// Конструктор. Создает обработчик событий мыши. PingPongGreenTable(){

PingPongGameEngine gameEngine = new PingPongGameEngine(this);

// Обрабатываем движения мыши для передвижения ракеток

addMouseMotionListener(gameEngine);

// Обрабатываем  события клавиатуры

addKeyListener(gameEngine);

}

// Добавитм в окно панель с JLabel

void addPaneltoFrame(Container container) {

container.setLayout(new BoxLayout(container,BoxLayout.Y_AXIS));

container.add(this);

label = new JLabel(

"Press N for a new game, S to serve or Q to quit");

container.add(label);

}

// Перерисовать окно. Этот метод вызывается виртуальной

// машиной, когда нужно обновить экран или

// вызывается метод repaint() из PingPointGameEngine

public void paintComponent(Graphics g) {

super.paintComponent(g);

// Нарисовать зеленый стол g.setColor(Color.GREEN); g.fillRect(0,0,TABLE_WIDTH,TABLE_HEIGHT);

// Нарисовать правую ракетку g.setColor(Color.yellow); g.fillRect(KID_RACKET_X, kidRacket_Y,

RACKET_WIDTH, RACKET_LENGTH);

// Нарисовать левую ракетку g.setColor(Color.blue); g.fillRect(COMPUTER_RACKET_X, computerRacket_Y,

RACKET_WIDTH,RACKET_LENGTH);

// Нарисовать мяч g.setColor(Color.red); g.fillOval(ballX,ballY,10,10);

// Нарисовать белые линии

g.setColor(Color.white); g.drawRect(10,10,300,200); g.drawLine(160,10,160,210);

// Установить фокус на стол, чтобы

// обработчик клавиатуры мог посылать команды столу

requestFocus();

}

// Установить текущее положение ракетки ребенка

public void setKidRacket_Y(int yCoordinate){

this.kidRacket_Y = yCoordinate;

repaint();

}

// Вернуть текущее положение ракетки ребенка

public int getKidRacket_Y(){

return kidRacket_Y;

}

// Установить текущее положение ракетки компьютера public void setComputerRacket_Y(int yCoordinate){

this.computerRacket_Y = yCoordinate;

repaint();

}

// Установить игровое сообщение

public void setMessageText(String text){

label.setText(text);

repaint();

}

// Установить позицию мяча  public void setBallPosition(int xPos, int yPos){

ballX=xPos; ballY=yPos; repaint();

}

public static void main(String[] args) {

// Создать экземпляр окна

JFrame f = new JFrame("Ping Pong Green Table");

// Убедиться, что окно может быть закрыто по нажатию на//крестик в углу   f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

PingPongGreenTable table = new PingPongGreenTable();

table.addPaneltoFrame(f.getContentPane());

// Установить размер окна и сделать его видимым f.setBounds(0,0,TABLE_WIDTH+5, TABLE_HEIGHT+40); f.setVisible(true);

}

} Я  добавил несколько final  переменных в интерфейс GameConstants, и  вы должны догадаться по их именам, для чего они нужны.package screens;

/**

*Этот интерфейс содержит все константы,которые используются в игре

*/

public interface GameConstants {

// Размеры стола

public final int TABLE_WIDTH =  320;

public final int TABLE_HEIGHT = 220; public final int TABLE_TOP = 12; public final int TABLE_BOTTOM = 180;

// Шаг перемещения мяча в пикселях   public final int BALL_INCREMENT = 4;

// Максимальные и минимальные координаты мяча public final int BALL_MIN_X = 1+ BALL_INCREMENT; public final int BALL_MIN_Y = 1 + BALL_INCREMENT;

public final int BALL_MAX_X = TABLE_WIDTH – BALL_INCREMENT;

public final int BALL_MAX_Y = TABLE_HEIGHT – BALL_INCREMENT;

// Начальные координаты мяча

public final int BALL_START_X = TABLE_WIDTH/2;

public final int BALL_START_Y = TABLE_HEIGHT/2;

//Размеры, расположения и шаг перемещения ракеток

public final int KID_RACKET_X = 300;

public final int KID_RACKET_Y_START = 100;

public final int COMPUTER_RACKET_X = 15;

public final int COMPUTER_RACKET_Y_START = 100;

public final int RACKET_INCREMENT = 2; public final int RACKET_LENGTH = 30; public final int RACKET_WIDTH = 5; public final int WINNING_SCORE = 21;

// Замедлить быстрые компьютеры – измените это значение,

// если понадобится

public final int SLEEP_TIME = 10; //время в миллисекундах

}

Ниже  я  перечислил  основные  изменения,  которые  сделал  в  классе PingPongGameEngine:?? Удалил интерфейс MouseListener  и все его методы, потому что мы  больше не обрабатываем клики мыши. Все движения мыши будут обрабатываться MouseMotionListener.?? Теперь этот класс реализует интерфейс Runnable, а вся логика находится  в методе  run(). Посмотрите на конструктор – там я создаю  и запускаю новый поток. Метод   run() обрабатывает правила    игры  в  несколько  шагов,  все  эти  шаги запрограммированы  внутри  условия if(ballServed).   Это сокращенный вариант выражения  if(ballServed==true).

?? Пожалуйста,    обратите    внимание    на    условие,    которое устанавливает  значение переменной canBounce в первом  шаге.  В зависимости от этого выражения, значение переменной будет  либо true, либо false.

?? Класс  реализует  интерфейс     KeyListener,   и  метод keyPressed()   проверяет, какая кнопка была нажата для начала/завершения  игры,   либо   подачи   мяча.   Этот   метод позволяет  обрабатывать как  заглавные, так  и маленькие буквы, например N  и n.?? Я  добавил  несколько private методов: displayScore(), kidServe()  и isBallOnTheTable().   Они   объявлены приватными,   потому что используются только внутри этого класса    и  другие  классы даже  не  подозревают  об  их существовании. Это пример инкапсуляции в действии.| ?? Некоторые   компьютеры  настолько   быстрые,   что контролировать  движение мяча становится трудно. Поэтому я замедлил  игру   с   помощью   метода   Thread.sleep(). Статический метод  sleep()  приостановит  текущий  поток на заданное в конструкторе количество миллисекунд.?? Чтобы  игра  стала  немного  веселее,  мяч  теперь  движется  по диагонали после удара ракеткой ребенка. Поэтому меняется не только X координата мяча, но и Y.package engine;

import java.awt.event.MouseMotionListener;

import java.awt.event.MouseEvent; import java.awt.event.KeyListener; import java.awt.event.KeyEvent; import screens.*;

/**

*Этот класс – обработчик событий мыши и клавиатуры.

* Рассчитывает движение мяча и ракеток,изменение их координат.

*/

}

// Обязательные методы из интерфейса MouseMotionListener

// (некоторые из них пустые,но должны быть включены все равно)

public void mouseDragged(MouseEvent e) {

}

public void mouseMoved(MouseEvent e) {

int mouse_Y = e.getY();

// Если мышь находится выше ракетки ребенка

// и не выходит за пределы стола – передвинуть ее вверх,

// в противном случае – опустить вниз

if (mouse_Y<kidRacket_Y && kidRacket_Y>TABLE_TOP){

kidRacket_Y -= RACKET_INCREMENT;

}elseif (kidRacket_Y < TABLE_BOTTOM) {

kidRacket_Y += RACKET_INCREMENT;

}

// Установить новое положение ракетки

table.setKidRacket_Y(kidRacket_Y);

}

// Обязательные методы из интерфейса KeyListener

public void  keyPressed(KeyEvent e){

char key = e.getKeyChar();

if (‘n’ == key || ‘N’ == key){

startNewGame();

} elseif (‘q’ == key || ‘Q’ == key){

endGame();

} elseif (‘s’ == key || ‘S’ == key){

kidServe();

}

}

public void keyReleased(KeyEvent e){}

public void keyTyped(KeyEvent e){}

// Начать новую игру

public void startNewGame(){

computerScore=0;

kidScore=0;

table.setMessageText("Score Computer: 0  Kid: 0");

kidServe();

}

// Завершить игру public void endGame(){

System.exit(0);

}

// Обязательный метод run() из интерфейса Runnable

public void run(){

boolean canBounce=false;

while (true) {

if(ballServed){ // если мяч движется     

//Шаг 1. Мяч движется влево?

if ( movingLeft && ballX > BALL_MIN_X){

canBounce = (ballY >= computerRacket_Y &&

ballY < (computerRacket_Y + RACKET_LENGTH)?true: false);

ballX-=BALL_INCREMENT;

// Добавить смещение вверх или вниз к любым

// движениям мяча влево или вправо

ballY-=verticalSlide;

table.setBallPosition(ballX,ballY);

// Может отскочить?

if (ballX <= COMPUTER_RACKET_X  && canBounce){

movingLeft=false;

}

}

// Шаг 2. Мяч движется вправо?

if ( !movingLeft && ballX <= BALL_MAX_X){

canBounce = (ballY >= kidRacket_Y && ballY <

(kidRacket_Y + RACKET_LENGTH)?true:false);

ballX+=BALL_INCREMENT;

table.setBallPosition(ballX,ballY);

// Может отскочить?

if (ballX >= KID_RACKET_X && canBounce){

movingLeft=true;

}

}

// Шаг 3. Перемещать ракетку компьютера вверх или вниз,

// чтобы блокировать мяч

if (computerRacket_Y < ballY

&& computerRacket_Y < TABLE_BOTTOM){

computerRacket_Y +=RACKET_INCREMENT;

}else if (computerRacket_Y > TABLE_TOP){

computerRacket_Y -=RACKET_INCREMENT;

}

table.setComputerRacket_Y(computerRacket_Y);

// Шаг 4. Приостановить

try {

Thread.sleep(SLEEP_TIME);

} catch (InterruptedException e) {

e.printStackTrace();

}

// Шаг 5. Обновить счет, если мячв зеленой области, но не движется

if (isBallOnTheTable()){

if (ballX > BALL_MAX_X ){

computerScore++;

displayScore();

}else if (ballX < BALL_MIN_X){

kidScore++;

displayScore();

}

}

} // Конец if ballServed

} // Конец while

}// Конец run()

// Подать с текущей позиции ракетки ребенка

private void kidServe(){

ballServed = true;

ballX = KID_RACKET_X-1;

ballY=kidRacket_Y;

if (ballY > TABLE_HEIGHT/2){

verticalSlide=-1;

}else{

verticalSlide=1;

}

table.setBallPosition(ballX,ballY);

table.setKidRacket_Y(kidRacket_Y);

}

private void displayScore(){

ballServed = false;

if (computerScore ==WINNING_SCORE){

table.setMessageText("Computer won! " + computerScore + ":" + kidScore);

}else if (kidScore ==WINNING_SCORE){

table.setMessageText("You won! "+ kidScore +

":" + computerScore);

}else{

table.setMessageText("Computer:  "+ computerScore +

" Kid: " + kidScore);

}

}

// Проверить, не пересек ли мяч верхнюю или нижнюю границу стола

private boolean isBallOnTheTable(){

if (ballY >= BALL_MIN_Y && ballY <= BALL_MAX_Y){

return true;

}else {

}

}

}

return false;

Поздравляю!   Вы   закончили   разработку   вашей   второй   игры. Скомпилируйте классы и играйте в нее.  После того, как вы разберетесь в коде, попробуйте изменить его – я уверен, у вас есть идеи, как сделать эту игру  лучше. Если хотите продолжить программировать игры, взгляните на Robocode –  этапрограммируемая  игра, позволяет изучать Java, создавая роботов:

http://robocode.sourceforge.net/?Open&ca=daw-­?prod-­?robocodeПособие  по сокетам в Java:

http://www.oracle.com/technetwork/java/socket-­?

140484.html

Введение  в потоки Java:

http://www-­?106.ibm.com/developerworks/edu/j-­?dw-­?

 javathread-­?i.htmlКласс java.awt.Graphics:

http://download.oracle.com/javase/6/docs/api/java/awt/G

raphics.html

 Практические задания1.  Класс  PingPongGameEngine устанавливает координаты белой точки  с  помощью  такого кода:

table.point.x = e.getX();

Сделайте  переменную point приватной в классе  PingPongGreenTable и добавьтеpublic  метод setPointCoordinates(int x, int y).

Используйте  этот метод в классеPingPongGameEngine.

2. В  нашей игре пинг-­?понг есть ошибка: после того, как один из игроков выиграл, все еще можно нажать кнопку S и игра продолжится. Исправьте  эту ошибку.

 Практические упражнения  для умников и умниц

1.    Попробуйте    поменять    значения RACKET_INCREMENT  и BALL_INCREMENT.  Чем больше  эти  значения,  тем  быстрее движется мяч и ракетки.   Поменяйте код так, чтобы пользователь  мог выбирать уровень от 1 до 10. Используйте  выбранные   значения   как коэффициент  движения для мяча и ракеток.

2. Когда ракетка ребенка отбивает мяч в верхней  части  стола,  мяч  отскакивает  по диагонали и быстро падает со стола. Измените программу  так,  чтобы  мяч  отскакивал  от верхней части стола по диагонали вниз, а от нижней части  стола диагонально вверх.

Источник: Java  Programming for Kids, Parents and Grandparents by Yakov Fain

По теме:

  • Комментарии