2013-11-24 5 views
2

для введения курса CS. Я пытаюсь сделать клон «Breakout» на Java. Игра выполнена на 99%, поэтому я подумал, что добавлю дополнительные дополнения.Клонирование Java «Breakout»: приостановка и возобновление потока

Одна вещь, которую я хотел добавить, - это возможность приостановить и возобновить использование пробела. Я добавил логическую переменную «isPaused» и меняю ее значение каждый раз, когда я вызываю game.resume() и game.suspend(). Затем я использую KeyAdapter, чтобы сообщить программе возобновить и приостановить на основе значения «isPaused», когда пользователь попадает в пробел. Это, похоже, работает большую часть времени, но иногда требуется два щелчка пробела. Я внимательно просмотрел код и не могу пощупать проблему. Как правило, это происходит, когда начинается новый уровень. Итак, я отправлю код из файла «Board.java», который содержит логику игры и проблему. Благодаря! Код ниже.

Этот класс «Совет» обрабатывает всю логику игры и отображает элементы на экране.

//imports 
import java.awt.*; 
import javax.swing.*; 
import java.util.Random; 
import java.lang.Thread; 
import javax.sound.sampled.*; 
import java.io.*; 
import java.awt.event.*; 
import java.util.ArrayList; 

//class definition 
public class Board extends JPanel implements Runnable, Constants { 
//variables 
Paddle paddle; 
Ball ball; 
Brick[][] brick = new Brick[10][5]; 
int score = 0, lives = 5, bricksLeft = 50, waitTime = 3, xSpeed, withSound, level = 1; 
String playerName; 
Thread game; 
String songFile = "music/Start.wav"; 
Color brickColor = new Color(0,0,255); 
ArrayList<Item> items = new ArrayList<Item>(); 
boolean isPaused = true; 

//constructor 
public Board(int width, int height) { 
    super.setSize(width, height); 
    addKeyListener(new BoardListener()); 
    setFocusable(true); 

    makeBricks(); 
    paddle = new Paddle(width/2, height-(height/10), width/7, height/50, Color.BLACK); 
    ball = new Ball(BALL_X_START, BALL_Y_START, BALL_WIDTH, BALL_HEIGHT, Color.BLACK); 

    //Get the player's name 
    playerName = JOptionPane.showInputDialog(null, "Enter your name:", "Name", JOptionPane.INFORMATION_MESSAGE); 
    if (playerName == null) { 
     System.exit(0); 
    } 

    //Start Screen that displays information and asks if the user wants music or not 
    String[] options = {"Yes", "No"}; 
    withSound = JOptionPane.showOptionDialog(null, "Brick Breaker, Version 1.0\nBy Ty-Lucas Kelley, for CSC 171 Fall 2013\nAll credit for the music goes to the SEGA Corporation.\n\n\nControls: Press spacebar to start, and use the arrow keys to move.\n\n\nWould you like to play with the music on?", "Introduction", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[1]); 
    playMusic(songFile, withSound); 

    game = new Thread(this); 
    game.start(); 
    stop(); 
} 

//fills the array of bricks 
public void makeBricks() { 
    for(int i = 0; i < 10; i++) { 
     for(int j = 0; j < 5; j++) { 
      Random rand = new Random(); 
      int itemType = rand.nextInt(3) + 1; 
      int numLives = 3; 
      brick[i][j] = new Brick(i * BRICK_WIDTH, (j * BRICK_HEIGHT) + (BRICK_HEIGHT/2), BRICK_WIDTH - 5, BRICK_HEIGHT - 4, brickColor, numLives, itemType); 
     } 
    } 
} 

//starts the thread 
public void start() { 
    game.resume(); 
    isPaused = false; 
} 

//stops the thread 
public void stop() { 
    game.suspend(); 
    isPaused = true; 
} 

//ends the thread 
public void destroy() { 
    game.resume(); 
    isPaused = false; 
    game.stop(); 
} 

//runs the game 
public void run() { 
    xSpeed = 1; 
    while(true) { 
     int x1 = ball.getX(); 
     int y1 = ball.getY(); 

     //Makes sure speed doesnt get too fast/slow 
     if (Math.abs(xSpeed) > 1) { 
      if (xSpeed > 1) { 
       xSpeed--; 
      } 
      if (xSpeed < 1) { 
       xSpeed++; 
      } 
     } 

     checkPaddle(x1, y1); 
     checkWall(x1, y1); 
     checkBricks(x1, y1); 
     checkLives(); 
     checkIfOut(y1); 
     ball.move(); 
     dropItems(); 
     checkItemList(); 
     repaint(); 

     try { 
      game.sleep(waitTime); 
     } catch (InterruptedException ie) { 
      ie.printStackTrace(); 
     } 
    } 
} 

public void addItem(Item i) { 
    items.add(i); 
} 

public void dropItems() { 
    for (int i = 0; i < items.size(); i++) { 
     Item tempItem = items.get(i); 
     tempItem.drop(); 
     items.set(i, tempItem); 
    } 
} 

public void checkItemList() { 
    for (int i = 0; i < items.size(); i++) { 
     Item tempItem = items.get(i); 
     if (paddle.caughtItem(tempItem)) { 
      items.remove(i); 
     } 
     else if (tempItem.getY() > WINDOW_HEIGHT) { 
      items.remove(i); 
     } 
    } 
} 

public void checkLives() { 
    if (bricksLeft == 0) { 
     ball.reset(); 
     bricksLeft = 50; 
     makeBricks(); 
     lives++; 
     level++; 
     repaint(); 
     stop(); 
    } 
    if (lives == 0) { 
     repaint(); 
     stop(); 
    } 
} 

public void checkPaddle(int x1, int y1) { 
    if (paddle.hitLeft(x1, y1)) { 
     ball.setYDir(-1); 
     xSpeed = -1; 
     ball.setXDir(xSpeed); 
    } 
    else if (paddle.hitRight(x1, y1)) { 
     ball.setYDir(-1); 
     xSpeed = 1; 
     ball.setXDir(xSpeed); 
    } 

    if (paddle.getX() <= 0) { 
     paddle.setX(0); 
    } 
    if (paddle.getX() + paddle.getWidth() >= getWidth()) { 
     paddle.setX(getWidth() - paddle.getWidth()); 
    } 
} 

public void checkWall(int x1, int y1) { 
    if (x1 >= getWidth() - ball.getWidth()) { 
     xSpeed = -Math.abs(xSpeed); 
     ball.setXDir(xSpeed); 
    } 
    if (x1 <= 0) { 
     xSpeed = Math.abs(xSpeed); 
     ball.setXDir(xSpeed); 
    } 
    if (y1 <= 0) { 
     ball.setYDir(1); 
    } 
    if (y1 >= getHeight()) { 
     ball.setYDir(-1); 
    } 
} 

public void checkBricks(int x1, int y1) { 
    for (int i = 0; i < 10; i++) { 
     for (int j = 0; j < 5; j++) { 
      if (brick[i][j].hitBottom(x1, y1)) { 
       ball.setYDir(1); 
       if (brick[i][j].isDestroyed()) { 
        bricksLeft--; 
        score += 50; 
        addItem(brick[i][j].item); 
       } 
      } 
      if (brick[i][j].hitLeft(x1, y1)) { 
       xSpeed = -xSpeed; 
       ball.setXDir(xSpeed); 
       if (brick[i][j].isDestroyed()) { 
        bricksLeft--; 
        score += 50; 
        addItem(brick[i][j].item); 
       } 
      } 
      if (brick[i][j].hitRight(x1, y1)) { 
       xSpeed = -xSpeed; 
       ball.setXDir(xSpeed); 
       if (brick[i][j].isDestroyed()) { 
        bricksLeft--; 
        score += 50; 
        addItem(brick[i][j].item); 
       } 
      } 
      if (brick[i][j].hitTop(x1, y1)) { 
       ball.setYDir(-1); 
       if (brick[i][j].isDestroyed()) { 
        bricksLeft--; 
        score += 50; 
        addItem(brick[i][j].item); 
       } 
      } 
     } 
    } 
} 

public void checkIfOut(int y1) { 
    if (y1 > PADDLE_Y_START) { 
     lives--; 
     score -= 100; 
     ball.reset(); 
     repaint(); 
     stop(); 
    } 
} 

//plays music throughout game if user wants to 
public void playMusic(String song, int yesNo) { 
    if (yesNo == 1) { 
     return; 
    } 
    else if (yesNo == -1) { 
     System.exit(0); 
    } 
    try { 
     AudioInputStream audio = AudioSystem.getAudioInputStream(new File(song).getAbsoluteFile()); 
     Clip clip = AudioSystem.getClip(); 
     clip.open(audio); 
     clip.loop(Clip.LOOP_CONTINUOUSLY); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

//fills the board 
@Override 
public void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    paddle.draw(g); 
    ball.draw(g); 

    for (int i = 0; i < 10; i++) { 
     for (int j = 0; j < 5; j++) { 
      brick[i][j].draw(g); 
     } 
    } 
    g.setColor(Color.BLACK); 
    g.drawString("Lives: " + lives, 10, getHeight() - (getHeight()/10)); 
    g.drawString("Score: " + score, 10, getHeight() - (2*(getHeight()/10)) + 25); 
    g.drawString("Level: " + level, 10, getHeight() - (3*(getHeight()/10)) + 50); 
    g.drawString("Player: " + playerName, 10, getHeight() - (4*(getHeight()/10)) + 75); 

    for (Item i: items) { 
     i.draw(g); 
    } 

    if (lives == 0) { 
     int heightBorder = getHeight()/10; 
     int widthBorder = getWidth()/10; 
     g.setColor(Color.BLACK); 
     g.fillRect(widthBorder, heightBorder, getWidth() - (2 * widthBorder), getHeight() - (2 * heightBorder)); 
     g.setColor(Color.WHITE); 
     g.drawString("Game Over! Click the Spacebar twice to start over.", getWidth()/5, getHeight()/2); 
    } 
} 

public String playerInfo() { 
    return rank(score) + "." + " Name: " + playerName + ", Score: " + score; 
} 

public int rank(int score) { 
    //check to see where this player falls on the list of saved games by reading from file 
    return 0; 
} 

public void saveGame() { 
    if (rank(score) >= 10) { 
     return; 
    } 
    //save this game to HighScores.txt 
} 

public void printScores() { 
    //print to paintComponent method. replace current 'game over' string 
} 

//Private class that handles gameplay and controls 
private class BoardListener extends KeyAdapter { 
    public void keyPressed(KeyEvent ke) { 
     int key = ke.getKeyCode(); 
     if (key == KeyEvent.VK_SPACE) { 
      if (lives > 0) { 
       if (!isPaused) { 
        stop(); 
       } 
       else { 
        start(); 
       } 
      } 
      else { 
       paddle.setWidth(getWidth()/7); 
       lives = 5; 
       score = 0; 
       bricksLeft = 50; 
       level = 1; 
       makeBricks(); 
       isPaused = true; 
       for (int i = 0; i < 10; i++) { 
        for (int j = 0; j < 5; j++) { 
         brick[i][j].setDestroyed(false); 
        } 
       } 
      } 
     } 
     if (key == KeyEvent.VK_LEFT) { 
      paddle.setX(paddle.getX() - 50); 
     } 
     if (key == KeyEvent.VK_RIGHT) { 
      paddle.setX(paddle.getX() + 50); 
     } 
    } 
    public void keyReleased(KeyEvent ke) { 
     int key = ke.getKeyCode(); 
     if (key == KeyEvent.VK_LEFT) { 
      paddle.setX(paddle.getX()); 
     } 
     if (key == KeyEvent.VK_RIGHT) { 
      paddle.setX(paddle.getX()); 
     } 
    } 
} 

}

+0

Пожалуйста, добавьте глобальный переменные ('game' ...) –

ответ

1

Ваша проблема, скорее всего, какие-то проблемы параллелизма. Вы можете переключить свою переменную от boolean до AtomicBoolean и использовать synchronized (game) в своих методах suspend и resume.

Также не следует использовать методы Thread.suspend и Thread.resume. Прочтите их JavaDoc для получения дополнительной информации.

Как это:

... 
AtomicBoolean isPaused = new AtomicBoolean(false); 
... 

private void gamePause() { 
    synchronized(game) { 
     game.isPaused.set(true); 
     game.notify(); 
    } 
} 

private void gameContinue() { 
    synchronized(game) { 
     game.isPaused.set(false); 
     game.notify(); 
    } 
} 
... 

Тогда в тех местах, где вы справляетесь цикл:

... 
public void run() { 
    xSpeed = 1; 
    while(true) { 
    synchronized(game) { 
     while(game.isPaused().get()) { 
     try { 
      Thread.sleep(1000); 
     } catch (InterruptedException iex) { 
      // This most likely means your JVM stops. Maybe log the Exception. 
      game.destroy(); 
      return; 
     } 
     } 
     int x1 = ball.getX(); 
     int y1 = ball.getY(); 
     ... 
     } 
    } 
    } 
} 

А также в методе checkLives. (Насколько я могу видеть checkLives вызывалась только из бега, если тот случай это уже в synchronized(game) блоке. Если нет, то вы должны добавить synchronized вокруг stop() здесь.

public void checkLives() { 
    if (bricksLeft == 0) { 
     ... 
     if(!game.isPaused().get()) 
      stop(); 
    } 
    if (lives == 0) { 
     repaint(); 
     if(!game.isPaused().get()) 
      stop(); 
    } 
} 

Проблема заключается в том что checkLives() вызовов stop() который вызывает isPaused быть перевернуты. Если в то же время KeyListener активируется опрашивается isPaused, считает, что игра приостановлена ​​и возобновляет его, таким образом, вы должны ударить пространство снова, чтобы продолжить.

+0

Включил его в AtomicBoolean, к сожалению все еще получая тот же результат ... два клика необходимы только после того, как по какой-то причине уровень увеличивается. – tylucaskelley

+0

@tylucaskelley Пожалуйста, посмотрите мой пример кода. Я посмотрю на метод изменения уровня. –

+0

@tylucaskelley Я добавил объяснение, почему pause/resume не работает должным образом при изменении уровня. –

Смежные вопросы