记忆游戏实现

时间:2021-04-13 03:55:09

标签: java swing user-interface

我已经开始玩一个非常基本的记忆游戏。

private void tbtnCard3ActionPerformed(java.awt.event.ActionEvent evt) {                                          
    tbtnCard3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Card3Logo.png")));
    if(tbtnCard5.isSelected()){
        score++;
        lblScore.setText(""+score);
    }
}                                         

private void tbtnCard4ActionPerformed(java.awt.event.ActionEvent evt) {                                          
     tbtnCard4.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Card7EWaste.png")));
    if(tbtnCard7.isSelected()){
        score++;
        lblScore.setText(""+score);
    }
}                                         

private void tbtnCard5ActionPerformed(java.awt.event.ActionEvent evt) {                                          
     tbtnCard5.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Card3Logo.png")));
    if(tbtnCard3.isSelected()){
        score++;
        lblScore.setText(""+score);
    }
}                                         

使用 Java Swing,我已经用我需要的图像设置了图标。在底部,我有一个按钮调用 start 以便首先显示所有按钮,直到按下 start btn。

 private void btnStartActionPerformed(java.awt.event.ActionEvent evt) {                                         
  tbtnCard1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));  
  tbtnCard2.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));    
  tbtnCard3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png"))); 
  tbtnCard4.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));  
  tbtnCard5.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));    
  tbtnCard6.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));   
  tbtnCard7.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));  
  tbtnCard8.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));    
  tbtnCard9.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));  
  tbtnCard10.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));  
  tbtnCard11.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));    
  tbtnCard12.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));  
  tbtnCard13.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));  
  tbtnCard14.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));    
  tbtnCard15.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png"))); 
  tbtnCard16.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));  
  tbtnCard17.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));    
  tbtnCard18.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));   
  tbtnCard19.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));  
  tbtnCard20.setIcon(new javax.swing.ImageIcon(getClass().getResource("/BlankImage.png")));    
}  

我唯一的问题是在玩家从一组 2 张中选错了一张牌后,两张牌都应该回到空白图像。我该怎么做?

1 个答案:

答案 0 :(得分:0)

这是一个简单记忆游戏的示例逻辑:

  1. 使用 JToggleButton 表示单元格。
  2. 将每个按钮的图标用于 隐藏 状态,将 selection icon 用于 显示 状态。
  3. 首先启用所有 JToggleButton 而未选中(隐藏状态)。
  4. 当用户选择 JToggleButton 时,它会自动更改为显示状态。
  5. 向所有 JToggleButton 添加相同的侦听器,以便随时跟踪最后两个选定的按钮。如果找到匹配项,则您可以禁用匹配按钮,如果未找到匹配项,则只需取消选择它们即可。

为了在以下演示代码中简单起见,我使用自定义 Icon 实现来实现上述所有目的,它只是绘制了某种颜色的圆角矩形。颜色用于将按钮标识为彼此匹配,即相同颜色的按钮表示选择了匹配。我只是在 隐藏 状态下为每个按钮使用了一个白色的圆角矩形,而在 显示 状态下使用了随机颜色。

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Random;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Main {
    
    private static class RoundRectIcon implements Icon {
        private final Color c;
        
        public RoundRectIcon(final Color c) {
            this.c = Objects.requireNonNull(c);
        }
        
        public Color getColor() {
            return c;
        }

        @Override
        public void paintIcon(final Component c,
                              final Graphics g,
                              final int x,
                              final int y) {
            g.setColor(this.c);
            g.fillRoundRect(x, y, getIconWidth() - 1, getIconHeight() - 1, getIconWidth() / 2, getIconHeight() / 2);
        }

        @Override
        public int getIconWidth() {
            return 50;
        }

        @Override
        public int getIconHeight() {
            return 50;
        }
    }
    
    private static class MemoryGameActionListener implements ActionListener {
        
        private final Timer revertTimer;
        private JToggleButton button1, button2;
        private int pairsCount;
        
        public MemoryGameActionListener(final int delay,
                                        final int pairsCount) {
            if (pairsCount <= 0)
                throw new IllegalArgumentException("Non positive pairsCount.");
            this.pairsCount = pairsCount;
            button1 = button2 = null;
            revertTimer = new Timer(delay, this);
            revertTimer.setRepeats(false);
        }
        
        @Override
        public void actionPerformed(final ActionEvent e) {
            revertTimer.stop();
            final Object source = e.getSource();
            if (source instanceof JToggleButton) {
                final JToggleButton button = (JToggleButton) source;
                if (button.isSelected()) {
                    if (button1 == null) //If we are in an initial state:
                        button1 = button; //Store the first clicked button.
                    else if (button2 == null) { //Else we have stored the first button, so store and check the second one...
                        button2 = button;
                        final RoundRectIcon icon1 = (RoundRectIcon) button1.getSelectedIcon(),
                                            icon2 = (RoundRectIcon) button2.getSelectedIcon();
                        if (Objects.equals(icon1.getColor(), icon2.getColor())) {
                            System.out.println("Pair of " + icon1.getColor() + " found!");
                            button1.setEnabled(false); //Don't let the user able to click it again.
                            button2.setEnabled(false); //Don't let the user able to click it again.
                            button1 = button2 = null; //A cycle is complete.
                            if (--pairsCount == 0)
                                JOptionPane.showMessageDialog(null, "You found every pair.", "You win", JOptionPane.INFORMATION_MESSAGE);
                        }
                        else {
                            System.out.println("Invalid match of " + icon1.getColor() + ", and " + icon2.getColor() + '.');
                            revertTimer.start(); //This timer will unselect the selected button pair after a delay.
                        }
                    }
                    else { //Else both buttons are stored, so this is the third one, so just ignore the previous buttons (if they did not match):
                        if (button1.isEnabled())
                            button1.setSelected(false);
                        if (button2.isEnabled())
                            button2.setSelected(false);
                        button1 = button; //Suppose now that the third clicked button is the first one.
                        button2 = null;
                    }
                }
                else { //Else the user unselected a button, so reset:
                    if (button1 != null) {
                        button1.setSelected(false);
                        button1 = null;
                    }
                    if (button2 != null) {
                        button2.setSelected(false);
                        button2 = null;
                    }
                }
            }
            else if (source == revertTimer) { //If a pair was found previously but it didn't match up, then reset:
                button1.setSelected(false);
                button2.setSelected(false);
                button1 = button2 = null;
            }
            else
                System.err.println("Unknown source " + source); //This should never happen.
        }
    }
    
    private static Color randomColor(final Random random) {
        final byte[] components = new byte[3];
        random.nextBytes(components);
        return new Color(components[0] & 0xFF, components[1] & 0xFF, components[2] & 0xFF);
    }
    
    private static HashSet<Color> randomUniqueColors(final int count) {
        final Random random = new Random();
        final HashSet<Color> colors = new HashSet<>(count);
        while (colors.size() < count) //If we produce the same color inside the loop, then 'colors.size()' will not increment.
            colors.add(randomColor(random));
        return colors;
    }
    
    private static JToggleButton createStandardToggleButton(final Color color,
                                                            final MemoryGameActionListener listener) {
        final JToggleButton toggle = new JToggleButton(new RoundRectIcon(Color.WHITE)); //Default (ie enabled + unselected) icon is just a white round rect.
        toggle.setSelectedIcon(new RoundRectIcon(color));
        toggle.setDisabledIcon(toggle.getSelectedIcon());
        toggle.setDisabledSelectedIcon(toggle.getSelectedIcon());
        toggle.addActionListener(listener);
        return toggle;
    }
    
    private static void createAndShowGUI() {
        
        final int rows = 4, cols = 4, delay = 1000;
        
        final int totalButtons = rows * cols; //The total number of buttons must be a multiple of 2 (because we use pairs of buttons).
        final int totalPairs = totalButtons / 2;
        final MemoryGameActionListener listener = new MemoryGameActionListener(delay, totalPairs);
        final JPanel grid = new JPanel(new GridLayout(0, cols, 5, 5));
        final ArrayList<JToggleButton> buttons = new ArrayList<>(totalButtons);
        randomUniqueColors(totalPairs).forEach(color -> {
            //Add the matching buttons to the list...
            buttons.add(createStandardToggleButton(color, listener));
            buttons.add(createStandardToggleButton(color, listener));
        });
        Collections.shuffle(buttons);
        buttons.forEach(button -> grid.add(button));
        
        final JFrame frame = new JFrame("Memory game");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(grid);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
    
    public static void main(final String[] args) {
        SwingUtilities.invokeLater(Main::createAndShowGUI);
    }
}

我首先生成所需颜色数量的 Set,然后为每种颜色创建两个按钮,然后将它们随机排列,最后将它们添加到 JPanelGridLayout

可以轻松调整上面的示例代码以包含您喜欢的任何 Icon,其中当然包括 ImageIcon 和其他自定义的。