You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Swing战舰游戏:JButton矩阵舰船放置事件响应问题求助

Java Swing战舰游戏舰船放置逻辑修复方案

问题核心

使用Java Swing开发战舰游戏,通过JFrame中两个二维JButton矩阵实现对战棋盘。当前舰船放置功能存在以下问题:

  • 点击“放置2格舰船”按钮后,无法正确响应矩阵内按钮的点击事件,无法触发相邻格子启用逻辑
  • 尝试用while循环等待按钮点击,导致Swing事件线程阻塞,界面无响应
  • 将放置逻辑移入ActionListener后,无法区分矩阵按钮点击属于2格舰船还是其他规格舰船的放置阶段

核心解决思路

通过状态变量跟踪当前舰船放置的阶段,替代阻塞式的循环等待。在ActionListener中根据当前状态执行对应逻辑,完全基于Swing事件驱动模型处理交互:

  1. 增加状态变量记录当前要放置的舰船尺寸、已放置部件数、第一个点击的棋盘按钮
  2. 点击舰船类型按钮时,初始化对应状态,激活棋盘按钮
  3. 点击棋盘按钮时,根据当前状态判断是放置第一个部件还是后续部件,验证相邻性后完成放置,状态流转或重置

修改后的完整代码示例

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

关键修改说明

  1. 状态变量引入:添加currentShipSize、placedParts、firstShipButton三个变量,精准跟踪当前放置状态
  2. 事件驱动逻辑重构:所有交互逻辑都在actionPerformed中完成,完全避免阻塞Swing事件线程,解决界面无响应问题
  3. 棋盘按钮交互区分:通过当前状态判断点击是放置第一个部件还是后续部件,自动触发相邻格子启用、相邻验证等逻辑
  4. 补全缺失方法:实现了findMatrixButton、enableAdjacentButtons、isAdjacent等核心辅助方法,完善功能逻辑
  5. 用户体验优化:添加弹窗提示放置结果,启用/禁用对应按钮避免误操作

内容的提问来源于stack exchange,提问作者Muizenpower222

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 00:24:53