生活游戏没有正确重置

Game of life not resetting properly

我正在尝试在单击重置按钮时重置我的生活游戏版本,但我遇到了问题。 单击按钮后,一切都成功重置,但我们看到世代移动的主要 Jpanel 没有。 我有两个 JLabel,一个显示当前代的数量,另一个显示该代中活细胞的数量。它们都已成功重置,但主要 JPanel 只是冻结,我再也看不到动画了。

GameOfLifeclass:

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 class:

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

知道为什么会这样吗?

你的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);
}

这个问题是新手常见的错误 -- 您认为更改变量引用会更改变量最初引用的先前对象。

具体来说,你有 body = new CellGrid(); 并且它所做的是让 body 变量引用一个新的 CellGrid 对象,但是(这是重要的部分),它*对 CellGrid 对象没有任何作用当前显示在您的 GUI 中,即先前引用的 body 变量。

几种替代解决方案:

  • 将 body 变量中现在引用的新 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

那么你需要做的就是在当前的CellGrid对象上调用reset(),然后调用repaint().

其他问题:

  • 您严重过度使用了静态修饰符。该程序中的任何内容都不应是静态的,除了 main 方法、您的常量,仅此而已。这对于这个小程序来说可能并不重要,但是当你以后尝试做单元测试或者扩展或增强这个程序,或者将它添加到另一个更大的程序时,它就会变得很重要。
  • 您在 Swing GUI 程序中使用 java.util.Timerjava.util.TimerTask 来 运行 动画循环,这样做是不安全的,因为这些 类 不是Swing 线程安全。最好使用 javax.swing.Timer 或 "Swing Timer" 代替这两个 类 到 运行 动画,因为这对于此 GUI 库是线程安全的。