生活游戏没有正确重置
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.Timer
和 java.util.TimerTask
来 运行 动画循环,这样做是不安全的,因为这些 类 不是Swing 线程安全。最好使用 javax.swing.Timer
或 "Swing Timer" 代替这两个 类 到 运行 动画,因为这对于此 GUI 库是线程安全的。
我正在尝试在单击重置按钮时重置我的生活游戏版本,但我遇到了问题。
单击按钮后,一切都成功重置,但我们看到世代移动的主要 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.Timer
和java.util.TimerTask
来 运行 动画循环,这样做是不安全的,因为这些 类 不是Swing 线程安全。最好使用javax.swing.Timer
或 "Swing Timer" 代替这两个 类 到 运行 动画,因为这对于此 GUI 库是线程安全的。