重新启动扫雷 GUI 程序

Restarting minesweeper GUI program

我制作了一个类似于扫雷的 GUI 游戏,尽管逻辑要少得多。基本上,用户单击 space 并查看下面是否有金子,如果没有,则显示 X。如果有,则显示金块。我想让游戏重新启动,一旦用户找到 10 块金币,所有数据都会重置。但是,我的 do while 循环没有成功。我已经注释掉了导致我出现问题的代码部分。每次我 运行 带有我已注释掉的内容的程序时,它都会卡住并且在我单击第一个按钮时不会继续 运行ning。否则,注释掉这些东西后,它会按应有的方式工作。

问:如何让我的程序在用户发现10个金币后重新启动并重置数据值?另外,为什么我设置的 12x12 按钮网格与我设置 2D 按钮阵列的方式不成比例?

这是我将 do while 注释掉后游戏的样子:

public class NuggetPanel extends JPanel {

    // variable declaration
    boolean restartGame = false;
    JButton[][] buttons = new JButton[12][12];
    JButton restart = new JButton("You win! Click to restart game");
    int count = 0;
    int goldFound = 0;
    JLabel clickCount = new JLabel("Number of digs: " + count);
    JLabel goldCount = new JLabel("Gold found: " + goldFound);


    // sets up the panel
    public NuggetPanel() {

        setLayout(new GridLayout(12, 12, 2, 2));
        JPanel LabelPane = new JPanel();


        //creates 2D array of buttons
        for (int i = 0; i < buttons.length; i++) {
            for (int j = 0; j < buttons[i].length; j++) {
                buttons[i][j] = new JButton("");
                buttons[i][j].addActionListener(new buttonListener());
                add(buttons[i][j]);
            }

        }



        //adds components
        LabelPane.add(clickCount);
        LabelPane.add(goldCount);
        LabelPane.add(restart);
        add(LabelPane);
        restart.setVisible(false);
    }

    // button is clicked
    private class buttonListener implements ActionListener {    

        @Override
        public void actionPerformed(ActionEvent e) {

//  do{

            // finds which button was clicked
            JButton buttonClicked = (JButton) e.getSource();

            restartGame = false;
            restart.setVisible(false);


            // sets up gold array
            Boolean[] randomGold = new Boolean[144];

            // fills gold array with 10 true elements
            for (int i = 0; i < randomGold.length; i++) {
                randomGold[i] = false;

                // sets 10 indexes in the array to true
                if (i == 10 || i == 20 || i == 30 || i == 40 || i == 50 || i == 60 || i == 70 || i == 80 || i == 90 || i == 100) {
                    randomGold[i] = true;
                }
            }

            // randomizes gold array
            Collections.shuffle(Arrays.asList(randomGold));

            // iterates through button array
            for (int i = 0; i < buttons.length; i++) {

                for (int j = 0; j < buttons[i].length; j++) {

                    if (buttonClicked == buttons[i][j]) {

                        // if there is a gold under the square, shows gold icon
                        if (randomGold[i] == true) {
                            buttons[i][j].setIcon(new ImageIcon("./src/Gold.jpg"));
                            buttons[i][j].removeActionListener(this);
                            goldFound++;
                            count++;
                            clickCount.setText("Number of digs: " + count);
                            goldCount.setText("Gold found: " + goldFound + "  ");

                        }

                        // if there is no gold under the square, shows X
                        else {
                            buttons[i][j].removeActionListener(this);
                            buttons[i][j].setIcon(new ImageIcon("./src/Missed.png"));
                            count++;
                            clickCount.setText("Number of digs: " + count);
                            goldCount.setText("Gold found: " + goldFound);
                        }

//                      if (goldFound == 10) {
//                          restart.setVisible(true);
//                      }
//
//                      if (buttonClicked == restart) {
//                          count = 0;
//                          goldFound = 0;
//                          clickCount.setText("Number of digs: " + count);
//                          for (int x = 0; i < buttons.length; i++)
//                          {
//                              for(int y = 0; j < buttons[i].length; j++)
//                              {
//                                  buttons[x][y].setIcon(null);
//                              }
//                          }
//                          restartGame = true;
//                      }

                        j = buttons[i].length - 1;
                        i = buttons.length - 1;

                    }

                }
            }
//      } while(restartGame = true);
    }
}
}

Also, why is my 12x12 button grid I've set up showing up not proportional to how I set up the 2D button array?

这是一个双重问题。 JButton 具有 margin 通常不是正方形的属性。此外,当您将组件添加到 GridLayout 时,所有组件的大小都相同,因此当您将 LabelPane 添加到布局时,它会强制所有按钮至少具有它的宽度。

相反,使用 GridLayout 将按钮放在单独的面板上,然后使用 BorderLayout 将其和 LabelPanel 放在 NuggetPanel 上,例如...

    public NuggetPanel() {

        setLayout(new BorderLayout());

        JPanel buttonPane = new JPanel(new GridLayout(12, 12, 2, 2));
        //creates 2D array of buttons
        for (int i = 0; i < buttons.length; i++) {
            for (int j = 0; j < buttons[i].length; j++) {
                // nb: I'd use a "blank" icon which was the same
                // size as the other icons and set it as the 
                // buttons icon, this will ensure that the 
                // buttons are always the right size...
                buttons[i][j] = new JButton("");
                buttons[i][j].setMargin(new Insets(5, 5, 5, 5));
                buttons[i][j].addActionListener(new buttonListener());
                buttonPane.add(buttons[i][j]);
            }

        }

        //adds components
        JPanel LabelPane = new JPanel();
        LabelPane.add(clickCount);
        LabelPane.add(goldCount);
        LabelPane.add(restart);

        add(buttonPane);
        add(LabelPane, BorderLayout.SOUTH);

        restart.setVisible(false);
    }

How can I make my program restart and the data values reset once a user uncovers 10 gold pieces?

这是一个更复杂的问题,需要您改变思维方式。

actionPerfomed 将在单击每个 ANY 按钮时调用,但您正在做的是随机化黄金的位置......这将使用户几乎不可能获胜。为什么?因为可以在已经检查过的按钮下放置一块金子...

    @Override
    public void actionPerformed(ActionEvent e) {

        // finds which button was clicked
        JButton buttonClicked = (JButton) e.getSource();

        restartGame = false;
        restart.setVisible(false);

        // sets up gold array
        Boolean[] randomGold = new Boolean[144];

        // fills gold array with 10 true elements
        for (int i = 0; i < randomGold.length; i++) {
            randomGold[i] = false;

            // sets 10 indexes in the array to true
            if (i == 10 || i == 20 || i == 30 || i == 40 || i == 50 || i == 60 || i == 70 || i == 80 || i == 90 || i == 100) {
                randomGold[i] = true;
            }
        }

        // randomizes gold array
        Collections.shuffle(Arrays.asList(randomGold));

相反,创建一个可用于重置或初始化游戏状态的方法...

    protected void reset() {
        // sets up gold array
        randomGold = new Boolean[144];

        // fills gold array with 10 true elements
        for (int i = 0; i < randomGold.length; i++) {
            randomGold[i] = false;

            // sets 10 indexes in the array to true
            if (i == 10 || i == 20 || i == 30 || i == 40 || i == 50 || i == 60 || i == 70 || i == 80 || i == 90 || i == 100) {
                randomGold[i] = true;
            }
        }

        // randomizes gold array
        Collections.shuffle(Arrays.asList(randomGold));

        for (int i = 0; i < buttons.length; i++) {
            for (int j = 0; j < buttons[i].length; j++) {
                // Reset the image icon with a blank image...
                buttons[i][j].setText("");
                buttons[i][j].setEnabled(true);
            }

        }
    }

这也应该在构造函数的末尾调用。这将确保游戏处于可以玩的就绪状态...

而不是使用 buttons[i][j].removeActionListener(this);,只需使用 buttons[i][j].setEnabled(false); 将按钮设置为禁用,这样可以更轻松地重置按钮,因为您无需关心按钮是否已经有ActionListener 是否与之关联(这可能导致您的 actionPerformed 方法被单个按钮多次调用 :P)

在您的 actionPerformed 方法结束时,您只需检查 goldFound 的数量并调用 reset 重置游戏...

if (goldFound == 10) {
    JOptionPane.showMessageDialog(NuggetPanel.this, "You win");
    reset();
}

其他...

不要使用诸如...

之类的方式从您的 src 目录加载资源
 buttons[i][j].setIcon(new ImageIcon("./src/Missed.png"));

一旦构建代码,这将失败。相反,您需要将资源作为嵌入式资源引用并使用 Class#getResource

 buttons[i][j].setIcon(new ImageIcon(getClass().getResource("/Missed.png")));

但坦率地说,这有点浪费,您可以改为将图标定义为常量....

public static final ImageIcon MISSED_ICON = new ImageIcon(getClass().getResource("/Missed.png")));

然后在需要时简单地引用它...

 buttons[i][j].setIcon(MISSED_ICON);

就我个人而言,我更喜欢 ImageIO.read,因为它抛出一个 IOException,但它使设置常量变得更加复杂...

此外,我可能会考虑使用某种 List 来管理按钮和 "gold" 位置,因为它只是使用 List#indexOf(Object) 然后必须 运行每次都通过双循环...