Доброго времени суток. Пример из книги, символ в символ, но не работает, шарик не перемещается. В чем причина? Исправлял на что ругается IDE, гуглил - всё бестолку.
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;
private Dimension preferredSize= new Dimension(TABLE_WIDTH,TABLE_HEIGHT);
// Устанавливаем размеры окна.Вызывается виртуальной машиной
public Dimension getPreferredSize() {
return preferredSize;
}
//Конструктор. Создает обработчик событий мыши.
public 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);
}
}
package engine;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import screens.*;
public class PingPongGameEngine implements Runnable,
MouseMotionListener, KeyListener, GameConstants {
private PingPongGreenTable table; // ссылка на стол
private int kidRacket_Y = KID_RACKET_Y_START;
private int computerRacket_Y = COMPUTER_RACKET_Y_START;
private int kidScore;
private int computerScore;
private int ballX; // координата X мяча
private int ballY; // координата Y мяча
private boolean movingLeft = true;
private boolean ballServed = false;
//Значение вертикального передвижения мяча в пикселях
private int verticalSlide;
// Конструктор. Содержит ссылку на объект стола
public PingPongGameEngine(PingPongGreenTable greenTable) {
table = greenTable;
Thread worker = new Thread(this);
worker.start();
}
// Обязательные методы из интерфейса MouseListener
// (некоторые из них пустые,но должны быть включены все равно)
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;
}else if(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();
} else if('q' == key || 'Q' == key) {
endGame();
} else if('s' == key || 'S' == key) {
kidServe();
}
}
public void keyReleased (KeyEvent e){}
public void keyTyped (KeyEvent e){}
// Начать новую игру
private void startNewGame() {
computerScore = 0;
kidScore = 0;
table.setMessageText("Score Computer: 0 Kid:0");
kidServe();
}
// Завершить игру
private 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;
}
}
}
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; //время в миллисекундах
}
Что за книга такая славная?
Тут классический пример отсутствия синхронизации между потоками. Весь UI крутится в одном потоке и обработка событий игры в другом (worker в твоём коде).
Когда в ты нажимаешь N, вызывается startNewGame() -> kidServe() -> ballServed=true;
, но другой поток не видит изменения переменной ballServed
, потому что он на уровне процессора закэшировал значение ballServed=false
.
Чтобы заставить процессор перечитывать значение переменной каждый раз, тебе надо объявить все переменные как volatile
, например private volatile boolean ballServed
.
Игра начнёт шевелиться, но работать правильно возможно не будет, потому что синхронизации между потоками всё ещё нет. Мне неохота весь код вычитывать, если какие-то переменные пишутся из обоих потоков, то это надо синхронизировать.
Я ещё AWT/Swing не очень хорошу помню - разве там можно из любого потока UI обновлять? см. https://dzone.com/articles/multi-threading-java-swing
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Подскажите пожалуйста как в Android на всех версиях получать ANDROID_ID, чтобы для конкретного устройства он некогда не менялсяИ на другом устройстве...
Возникла необходимость получить в Java-классе информацию об инстансе WildFly, на котором развернуто приложение (в идеале - имя сервера, либо любую...
Это запрос на удаление дубликатов