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

Java Graphics2D如何实现点击后移动BufferedImage棋子

棋子移动不生效的核心原因
  • paintComponent方法硬编码了初始棋子的绘制逻辑,完全没有读取piecePosition里存储的动态棋子位置数据,不管你怎么修改位置数据,重绘时永远只会在初始开局位置画棋子。
  • makeChanges方法没有实现核心的位置更新逻辑,既没有修改piecePosition里的棋子对应位置,也没有调用repaint()触发界面重绘,移动操作的逻辑没有闭环。
  • 坐标计算存在混用宽高的问题,计算纵向格子坐标时错误使用了宽度作为除数,边界情况会算错格子位置。
  • 鼠标按下时错误修改了整个JPanel的背景色,棋盘格子的颜色本身是在paintComponent里通过填充矩形实现的,修改面板根背景不会产生选中格子高亮的效果。
修复方案

1. 重构棋子绘制逻辑

删除paintComponent里所有硬编码判断y==0、y==1、y==6、y==7绘制棋子的冗余代码,改为遍历piecePosition动态绘制每个格子的棋子,同时补全Graphics2D资源释放逻辑:

@Override
protected void paintComponent (Graphics graphic){
    super.paintComponent(graphic);
    Graphics2D g2d = (Graphics2D) graphic.create();
    // 开抗锯齿让画面更平滑
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    boolean cellColorToggle = true;
    int cellSize = getWidth() / 8;
    g2d.setFont(font.deriveFont(Font.PLAIN, 18)); // 用你加载的自定义字体
    for(int y = 0; y < 8; y++){
        for(int x = 0; x < 8; x++){
            // 画棋盘格子
            if(cellColorToggle){
                g2d.setColor(Color.decode("#f7e7ce"));
            } else {
                g2d.setColor(Color.decode("#B58863"));
            }
            g2d.fillRect(x*cellSize, y*cellSize, cellSize, cellSize);

            // 画坐标标注
            if(y == 7){
                g2d.setColor(Color.BLACK);
                g2d.drawString(String.valueOf(x_AxisMap.get(x)), x*cellSize+3, y*cellSize + cellSize - 3);
            }
            if(x == 7){
                g2d.setColor(Color.BLACK);
                g2d.drawString(String.valueOf(y_AxisMap.get(y)), x*cellSize + cellSize - 15, y*cellSize + 15);
            }

            // 动态画棋子
            int pos = positions[y][x];
            String pieceInfo = piecePosition.get(pos);
            if(pieceInfo != null){
                String[] split = pieceInfo.split(" ");
                String pieceColor = split[0];
                String pieceType = split[1];
                BufferedImage targetPiece = null;
                switch (pieceType){
                    case "ROOK":
                        targetPiece = pieceColor.equals("white") ? Piece.ROOK.white() : Piece.ROOK.black();
                        break;
                    case "KNIGHT":
                        targetPiece = pieceColor.equals("white") ? Piece.KNIGHT.white() : Piece.KNIGHT.black();
                        break;
                    case "BISHOP":
                        targetPiece = pieceColor.equals("white") ? Piece.BISHOP.white() : Piece.BISHOP.black();
                        break;
                    case "QUEEN":
                        targetPiece = pieceColor.equals("white") ? Piece.QUEEN.white() : Piece.QUEEN.black();
                        break;
                    case "KING":
                        targetPiece = pieceColor.equals("white") ? Piece.KING.white() : Piece.KING.black();
                        break;
                    case "PAWN":
                        targetPiece = pieceColor.equals("white") ? Piece.PAWN.white() : Piece.PAWN.black();
                        break;
                }
                if(targetPiece != null){
                    g2d.drawImage(targetPiece, x*cellSize, y*cellSize, cellSize, cellSize, this);
                }
            }
            cellColorToggle = !cellColorToggle;
        }
        cellColorToggle = !cellColorToggle;
    }
    g2d.dispose(); // 释放创建的Graphics2D资源避免内存泄漏
}

2. 补全移动逻辑

修改makeChanges方法,实现棋子位置更新、界面重置和重绘触发:

public void makeChanges(){
    // 把之前乱改的面板背景重置
    setBackground(Color.decode("#ebe4e4"));
    if(pressedPosition == releasedPosition) {
        repaint();
        return;
    }
    // 获取要移动的棋子
    String movePiece = piecePosition.get(pressedPosition);
    if(movePiece == null) {
        repaint();
        return;
    }
    // 这里可以加国际象棋规则判断,比如是否允许走这个位置、是否是己方回合、是否将军之类的
    // 更新位置映射:起点清空,终点放置移动的棋子(实现吃子直接覆盖即可)
    piecePosition.put(pressedPosition, null);
    piecePosition.put(releasedPosition, movePiece);
    // 清空临时存储的选中棋子数据
    pressedPiece = null;
    releasedPiece = null;
    // 触发重绘
    repaint();
}

3. 修正坐标计算错误

把mousePressed和mouseReleased里的坐标计算统一用格子边长,避免宽高混用:

// mousePressed里的坐标计算
int cellSize = getWidth()/8;
pressedX = e.getX() / cellSize;
pressedY = e.getY() / cellSize;
pressedPosition = getNumberPosition(pressedX, pressedY);

// mouseReleased里的坐标计算
int cellSize = getWidth()/8;
releasedX = e.getX() / cellSize;
releasedY = e.getY() / cellSize;
releasedPosition = getNumberPosition(releasedX, releasedY);

其他优化点

  • 加载自定义字体后,需要注册到本地图形环境才能正常显示,在字体加载的try块里加一行:
    GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);
    
  • 如果要实现选中格子高亮的效果,不要改面板背景,加一个成员变量存储选中的格子坐标,在paintComponent里对应位置画一个半透明的高亮矩形即可。
  • 现在的棋子图片没有指定绘制大小,调用drawImage的时候传入cellSize作为宽高,棋子会自动适配格子大小,不会出现缩放错位。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 02:42:27