Java Swing战舰游戏:JButton矩阵舰船放置事件响应问题求助
Java Swing战舰游戏舰船放置逻辑修复方案
问题核心
使用Java Swing开发战舰游戏,通过JFrame中两个二维JButton矩阵实现对战棋盘。当前舰船放置功能存在以下问题:
- 点击“放置2格舰船”按钮后,无法正确响应矩阵内按钮的点击事件,无法触发相邻格子启用逻辑
- 尝试用
while循环等待按钮点击,导致Swing事件线程阻塞,界面无响应 - 将放置逻辑移入
ActionListener后,无法区分矩阵按钮点击属于2格舰船还是其他规格舰船的放置阶段
核心解决思路
通过状态变量跟踪当前舰船放置的阶段,替代阻塞式的循环等待。在ActionListener中根据当前状态执行对应逻辑,完全基于Swing事件驱动模型处理交互:
- 增加状态变量记录当前要放置的舰船尺寸、已放置部件数、第一个点击的棋盘按钮
- 点击舰船类型按钮时,初始化对应状态,激活棋盘按钮
- 点击棋盘按钮时,根据当前状态判断是放置第一个部件还是后续部件,验证相邻性后完成放置,状态流转或重置
修改后的完整代码示例
import java.awt.Color; import java.awt.event.*; import javax.swing.*; // 补全自定义Button类的实现(原代码缺失) class Button { private JButton btn; private boolean isShip; private boolean isActive; private int row; private int col; public Button(int x, int y, int width, int height) { btn = new JButton(); btn.setBounds(x, y, width, height); btn.setBackground(Color.LIGHT_GRAY); } public JButton getButton() { return btn; } public void setShip(boolean ship) { isShip = ship; if (ship) { btn.setBackground(Color.BLUE); } } public void setActive(boolean active) { isActive = active; btn.setEnabled(active); } public void setRowCol(int row, int col) { this.row = row; this.col = col; } public int getRow() { return row; } public int getCol() { return col; } } public class MyFrame extends JFrame implements ActionListener{ Button[][] matrixOp = new Button[10][10]; Button[][] matrixPl = new Button[10][10]; private JButton lastClickedButton = null; JButton start; JButton ship2; JButton ship3; JButton ship32; JButton ship4; JButton ship5; // 状态变量:跟踪当前舰船放置阶段 private int currentShipSize = 0; private int placedParts = 0; private Button firstShipButton = null; public MyFrame(){ this.setTitle("BattleShip"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(600, 700); // 调整窗口尺寸避免控件重叠 this.setLayout(null); this.setLocationRelativeTo(null); // 开始按钮 start = new JButton("Start"); start.setBounds(0, 280, 80, 30); start.setBackground(Color.green); start.addActionListener(this); this.add(start); // 舰船类型按钮 ship2 = new JButton("2格船"); ship2.setBounds(90, 280, 80, 30); ship2.setBackground(new Color(152, 151, 169)); ship2.setEnabled(false); ship2.addActionListener(this); this.add(ship2); ship3 = new JButton("3格船"); ship3.setBounds(180, 280, 80, 30); ship3.setBackground(new Color(152, 151, 169)); ship3.setEnabled(false); ship3.addActionListener(this); this.add(ship3); ship32 = new JButton("3格船(2)"); ship32.setBounds(270, 280, 80, 30); ship32.setBackground(new Color(152, 151, 169)); ship32.setEnabled(false); ship32.addActionListener(this); this.add(ship32); ship4 = new JButton("4格船"); ship4.setBounds(360, 280, 80, 30); ship4.setBackground(new Color(152, 151, 169)); ship4.setEnabled(false); ship4.addActionListener(this); this.add(ship4); ship5 = new JButton("5格船"); ship5.setBounds(450, 280, 80, 30); ship5.setBackground(new Color(152, 151, 169)); ship5.setEnabled(false); ship5.addActionListener(this); this.add(ship5); // 创建玩家棋盘按钮 for(int row = 0; row < 10; row++){ for(int col = 0; col < 10; col++){ int rp = 320 + row * 26; int cp = 14 + col * 26; Button b = new Button(cp, rp, 26, 26); b.setRowCol(row, col); b.setActive(false); b.getButton().addActionListener(this); matrixPl[row][col] = b; this.add(b.getButton()); } } this.setVisible(true); } // 放置单个舰船部件 private void placeShipPart(Button btn) { btn.setShip(true); btn.setActive(false); } // 激活/禁用玩家棋盘所有按钮 private void setPlayerBoardEnabled(boolean enabled) { for(int row = 0; row < 10; row++){ for(int col = 0; col < 10; col++){ matrixPl[row][col].setActive(enabled && !matrixPl[row][col].getButton().getBackground().equals(Color.BLUE)); } } } // 启用第一个按钮的相邻格子 private void enableAdjacentButtons(Button firstBtn) { int row = firstBtn.getRow(); int col = firstBtn.getCol(); // 上 if(row > 0) matrixPl[row-1][col].setActive(true); // 下 if(row < 9) matrixPl[row+1][col].setActive(true); // 左 if(col > 0) matrixPl[row][col-1].setActive(true); // 右 if(col < 9) matrixPl[row][col+1].setActive(true); } // 判断两个按钮是否相邻 private boolean isAdjacent(Button btn1, Button btn2) { int rowDiff = Math.abs(btn1.getRow() - btn2.getRow()); int colDiff = Math.abs(btn1.getCol() - btn2.getCol()); return (rowDiff == 1 && colDiff == 0) || (rowDiff == 0 && colDiff == 1); } // 根据按钮找到对应的Button对象 private Button findMatrixButton(JButton swingBtn) { for(int row = 0; row < 10; row++){ for(int col = 0; col < 10; col++){ if(matrixPl[row][col].getButton() == swingBtn){ return matrixPl[row][col]; } } } return null; } // 重置放置状态 private void resetPlacementState() { currentShipSize = 0; placedParts = 0; firstShipButton = null; setPlayerBoardEnabled(false); } @Override public void actionPerformed(ActionEvent e){ Object source = e.getSource(); // 开始按钮逻辑 if(source == start){ start.setEnabled(false); // 启用所有舰船按钮 ship2.setEnabled(true); ship3.setEnabled(true); ship32.setEnabled(true); ship4.setEnabled(true); ship5.setEnabled(true); } // 舰船类型按钮逻辑 else if(source == ship2){ currentShipSize = 2; placedParts = 0; firstShipButton = null; setPlayerBoardEnabled(true); // 禁用其他舰船按钮,避免切换 ship3.setEnabled(false); ship32.setEnabled(false); ship4.setEnabled(false); ship5.setEnabled(false); } // 同理添加其他舰船按钮的逻辑 else if(source == ship3){ currentShipSize = 3; placedParts = 0; firstShipButton = null; setPlayerBoardEnabled(true); ship2.setEnabled(false); ship32.setEnabled(false); ship4.setEnabled(false); ship5.setEnabled(false); } // 棋盘按钮点击逻辑 else { Button clickedBtn = findMatrixButton((JButton)source); if(clickedBtn == null || currentShipSize == 0) return; if(placedParts == 0){ // 放置第一个部件 placeShipPart(clickedBtn); firstShipButton = clickedBtn; placedParts++; // 禁用所有按钮,只启用相邻格子 setPlayerBoardEnabled(false); enableAdjacentButtons(firstShipButton); } else { // 验证是否相邻 if(!isAdjacent(firstShipButton, clickedBtn)){ JOptionPane.showMessageDialog(this, "请选择相邻格子!"); return; } // 放置第二个部件 placeShipPart(clickedBtn); placedParts++; // 判断是否完成舰船放置 if(placedParts == currentShipSize){ JOptionPane.showMessageDialog(this, currentShipSize + "格舰船放置完成!"); resetPlacementState(); // 重新启用所有舰船按钮 ship2.setEnabled(true); ship3.setEnabled(true); ship32.setEnabled(true); ship4.setEnabled(true); ship5.setEnabled(true); } } } } public static void main(String[] args) { SwingUtilities.invokeLater(MyFrame::new); } }
关键修改说明
- 状态变量引入:添加
currentShipSize、placedParts、firstShipButton三个变量,精准跟踪当前放置状态 - 事件驱动逻辑重构:所有交互逻辑都在
actionPerformed中完成,完全避免阻塞Swing事件线程,解决界面无响应问题 - 棋盘按钮交互区分:通过当前状态判断点击是放置第一个部件还是后续部件,自动触发相邻格子启用、相邻验证等逻辑
- 补全缺失方法:实现了
findMatrixButton、enableAdjacentButtons、isAdjacent等核心辅助方法,完善功能逻辑 - 用户体验优化:添加弹窗提示放置结果,启用/禁用对应按钮避免误操作
内容的提问来源于stack exchange,提问作者Muizenpower222
相关产品推荐
相关产品推荐

