Игра жизни не сбрасывается должным образом - PullRequest
0 голосов
/ 06 февраля 2020

Я пытаюсь сбросить мою версию игры жизни при нажатии кнопки сброса, но у меня есть проблема. После нажатия кнопки все успешно сбрасывается, но не основной Jpanel, где мы видим движение поколений. У меня есть два JLabel, один показывает номер текущего поколения, а другой показывает количество живых клеток в этом поколении. Они оба успешно сброшены, но основной JPanel просто зависает, и я больше не вижу анимацию.

Класс GameOfLife:

public class GameOfLife extends JFrame implements ActionListener {

private static class GameStep extends TimerTask {
    static GameOfLife life = new GameOfLife();

    @Override
    public void run() {
        updateLabels();
    }
}

static JLabel aliveLabel = new JLabel("Alive:");
static JLabel GenerationLabel = new JLabel("Generation #");
static CellGrid body = new CellGrid();
static JPanel header = new JPanel();
static int genNumber = 1;
static JButton PlayToggleButton = new JButton("pause");
static JButton ResetButton = new JButton("reset");
static Boolean isPaused = false;
static GameStep game = new GameStep();
static Timer timer = new Timer();


public GameOfLife() {
    super("Game of life");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(700, 660);
    setLocationRelativeTo(null);
    setLayout(new FlowLayout());

    GenerationLabel.setName("GenerationLabel");
    aliveLabel.setName("aliveLabel");
    PlayToggleButton.setName("PlayToggleButton");
    ResetButton.setName("ResetButton");

    PlayToggleButton.addActionListener(this);
    ResetButton.addActionListener(this);

    PlayToggleButton.setIcon(new ImageIcon(play));
    ResetButton.setIcon(new ImageIcon(reset));

    PlayToggleButton.setPreferredSize(new Dimension(40,30));
    ResetButton.setPreferredSize(new Dimension(40,30));

    header.setLayout(new FlowLayout());
    header.setPreferredSize(new Dimension(100, this.getHeight()));
    header.add(PlayToggleButton);
    header.add(ResetButton);
    header.add(GenerationLabel);
    header.add(aliveLabel);

    body.setLayout(new BorderLayout());
    body.setPreferredSize(new Dimension(500, this.getHeight()));

    add(header, BorderLayout.WEST);
    add(body, BorderLayout.CENTER);
    setVisible(true);

}

public static void updateLabels(){
    body.run();
    GenerationLabel.setText("Generation #"+ genNumber++);
    aliveLabel.setText("Alive: "+ body.totalAlive());
}

@Override
public void actionPerformed(ActionEvent e) {

    if(e.getActionCommand().equals("pause")){
        pauseResume();
    }
    else if(e.getActionCommand().equals("reset")){
        reset();
    }
}

static void loopStep(){
    timer.schedule(game, 0,1000);
}

static void pauseResume() {

    if(!isPaused){
        isPaused = true;
        timer.cancel();
    }
    else{
        isPaused = false;
        timer = new Timer();
        timer.schedule(new GameStep(), 0,1000);
    }
}
static void reset() {
    timer.cancel();
    isPaused = false;

    genNumber = 1;
    header = new JPanel();
    body = new CellGrid();
    body.repaint();

    timer = new Timer();
    timer.schedule(new GameStep(), 0,1000);
}

public static void main(String[] args) {
   loopStep();
}
}

Класс CellGrid:

public class CellGrid extends JPanel implements Runnable{
private static final int ROWS = 60;
private static final int COLS = 60;
private static final int CELL_WIDTH = 10;
private static Cell[][] cellGrid = new Cell[ROWS][COLS];

public CellGrid() {
    for (int row = 0; row < cellGrid.length; row++) {
        for (int col = 0; col < cellGrid[row].length; col++) {
            int x = col * CELL_WIDTH;
            int y = row * CELL_WIDTH;
            cellGrid[row][col] = new Cell(x, y, CELL_WIDTH);

            if (new Random().nextBoolean()) {
                cellGrid[row][col].setAlive(true);
            } else {
                cellGrid[row][col].setAlive(false);
            }
        }
    }
}
public int totalAlive(){
    int totalAlive = 0;
    for (Cell[] cells : cellGrid) {
        for (int j = 0; j < cellGrid.length; j++) {
            if (cells[j].isAlive())
                totalAlive++;
        }
    }
    return totalAlive;
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    for (Cell[] cellRow : cellGrid) {
        for (Cell cell : cellRow) {
            cell.draw(g2);
        }
    }
}

@Override
public void run() {
    cellGrid = new GenerationMaker4().nextGeneration(cellGrid);
    repaint();
}
}

есть идеи, почему это происходит?

1 Ответ

2 голосов
/ 06 февраля 2020

Ваш reset() метод:

static void reset() {
    timer.cancel();
    isPaused = false;

    genNumber = 1;
    header = new JPanel();
    body = new CellGrid();
    body.repaint();

    timer = new Timer();
    timer.schedule(new GameStep(), 0,1000);
}

Проблема - распространенная ошибка новичка ie - вы думаете, что изменение ссылки на переменную изменит предыдущий объект, на который изначально ссылалась переменная.

В частности, у вас есть body = new CellGrid();, и это означает, что переменная тела ссылается на новый объект CellGrid, но (и это важная часть), она * ничего не делает с объектом CellGrid, который в данный момент отображается в вашем GUI, на который ранее ссылалась переменная тела.

Несколько альтернативных решений:

  • Добавьте новый объект CellGrid, на который теперь ссылается переменная тела, в GUI в той же позиции BorderLayout, покрывая предыдущую
  • Еще лучше не создавать новый объект CellGrid, а вместо этого создать способ вернуть текущий CellGrid в исходное состояние.

Например, возможно, если вы изменили CellGrid на ...

public class CellGrid extends JPanel implements Runnable{
    private static final int ROWS = 60;
    private static final int COLS = 60;
    private static final int CELL_WIDTH = 10;
    private Cell[][] cellGrid = new Cell[ROWS][COLS]; // make this non-static

    public CellGrid() {
        reset();
    }

    public void reset() {   
        cellGrid = new Cell[ROWS][COLS];
        for (int row = 0; row < cellGrid.length; row++) {
            for (int col = 0; col < cellGrid[row].length; col++) {
                int x = col * CELL_WIDTH;
                int y = row * CELL_WIDTH;
                cellGrid[row][col] = new Cell(x, y, CELL_WIDTH);

                if (new Random().nextBoolean()) {
                    cellGrid[row][col].setAlive(true);
                } else {
                    cellGrid[row][col].setAlive(false);
                }
            }
        }
    }

// ..... more code below

Затем все, что вам нужно сделать, это вызвать reset() для текущего объекта CellGrid, а затем вызвать repaint().

Другие проблемы:

  • Вы чрезмерно используете модификатор stati c. Ничто в этой программе не должно быть stati c, кроме основного метода, ваших констант и все. Это может быть не важно для этой небольшой программы, но это станет важным позже, когда вы попытаетесь выполнить модульное тестирование или расширить или усовершенствовать эту программу или добавить ее в другую большую программу.
  • Вы используете java.util.Timer и java.util.TimerTask для запуска анимации l oop в программе Swing GUI, и это небезопасно, поскольку эти классы не безопасны для потоков Swing. Гораздо лучше использовать javax.swing.Timer или «Swing Timer» вместо обоих этих классов для запуска анимации, так как это потокобезопасно для этой библиотеки GUI.
...