Java ACM库移动GObject时残留黑线拖影问题排查
问题描述
实现打砖块游戏的矩形GObject挡板跟随鼠标在画布移动的效果时,程序运行后移动矩形会在移动路径上留下黑色线条,已知问题和未移除旧绘制矩形实例相关,原有实现代码如下:
// TODO: comment this program import acm.graphics.*; // GOval, GRect, etc. import acm.program.*; // GraphicsProgram import acm.util.*; // RandomGenerator import java.awt.*; // Color import java.awt.event.*; // MouseEvent public class Breakout extends BreakoutProgram { public void run() { setCanvasSize(CANVAS_WIDTH, CANVAS_HEIGHT); brickBuilder(); } // 构建砖块阵列,按行设置砖块颜色 private void brickBuilder() { int i=0;//列计数 int j=-1;//行计数,初始值设为-1避免y坐标和颜色匹配逻辑出错 while(j<(NBRICK_ROWS-1)) { j++; i=0;//每次行循环重置列计数 double x_coordinate = getWidth()/2-(NBRICK_COLUMNS*BRICK_WIDTH+((NBRICK_COLUMNS-1)*BRICK_SEP))/2; double y_coordinate = BRICK_Y_OFFSET; y_coordinate +=j*(BRICK_HEIGHT+BRICK_SEP); while(i<NBRICK_COLUMNS) { i++; x_coordinate +=(BRICK_WIDTH+BRICK_SEP); GRect brickWall = new GRect(x_coordinate,y_coordinate,BRICK_WIDTH,BRICK_HEIGHT); brickWall.setFilled(true); switch((j+1)%10) {//按行循环设置砖块颜色 case 1: brickWall.setFillColor(Color.RED); break; case 2: brickWall.setFillColor(Color.RED); break; case 3: brickWall.setFillColor(Color.ORANGE); break; case 4: brickWall.setFillColor(Color.ORANGE); break; case 5: brickWall.setFillColor(Color.YELLOW); break; case 6: brickWall.setFillColor(Color.YELLOW); break; case 7: brickWall.setFillColor(Color.GREEN); break; case 8: brickWall.setFillColor(Color.GREEN); break; case 9: brickWall.setFillColor(Color.CYAN); break; case 0: brickWall.setFillColor(Color.CYAN); break; } add(brickWall); } } } public void mouseMoved(MouseEvent e) { GRect paddle = new GRect(e.getX(), getHeight()-PADDLE_Y_OFFSET, PADDLE_WIDTH, PADDLE_HEIGHT); paddle.setFilled(true); add(paddle); } public void init() { addMouseListeners(); } }
初步判断问题出在mouseMoved和init方法的实现逻辑上,需要定位具体错误原因,给出可运行的正确实现方案。
错误原因定位
init方法逻辑无问题:addMouseListeners()是ACM库注册鼠标事件监听的标准写法,不需要修改。- 核心错误在
mouseMoved方法:每次鼠标移动触发事件时,代码都会新建一个GRect挡板实例并添加到画布上,但从未删除之前生成的旧挡板对象。所有历史绘制的挡板会一直保留在画布上,连续排列就形成了路径上的黑色拖影。 - 存在额外交互缺陷:现有逻辑没有做挡板的边界限制,鼠标移动到画布边缘时,挡板会整体或部分移出可视区域,不符合打砖块游戏的交互规则。
正确实现方案
实现逻辑调整如下:
- 将挡板声明为类的成员变量,仅在程序初始化阶段创建1次挡板实例,不要在鼠标移动事件里重复新建对象
- 鼠标移动时仅更新已有挡板的坐标位置,不需要重复执行
add()操作添加对象 - 增加x坐标边界判断,保证挡板始终完整显示在画布范围内
具体修改步骤:
- 首先在类内部声明挡板成员变量:
private GRect paddle;
- 在
run()方法中,调用brickBuilder()完成砖块绘制后,初始化挡板并添加到画布:
// 初始化挡板,y坐标固定在距离底部PADDLE_Y_OFFSET的位置 paddle = new GRect(0, getHeight() - PADDLE_Y_OFFSET, PADDLE_WIDTH, PADDLE_HEIGHT); paddle.setFilled(true); add(paddle);
- 重写
mouseMoved方法,替换原有重复新建对象的逻辑:
public void mouseMoved(MouseEvent e) { // 计算挡板x坐标,默认让挡板中心对齐鼠标位置 double paddleX = e.getX() - PADDLE_WIDTH / 2; // 左边界限制:挡板最左侧不能超出画布左边缘 if (paddleX < 0) { paddleX = 0; } // 右边界限制:挡板最右侧不能超出画布右边缘 if (paddleX > getWidth() - PADDLE_WIDTH) { paddleX = getWidth() - PADDLE_WIDTH; } // 直接更新挡板位置,不需要新建对象 paddle.setLocation(paddleX, getHeight() - PADDLE_Y_OFFSET); }
修改后运行程序,挡板会平滑跟随鼠标移动,不会再留下黑色拖影,同时不会超出画布边界。
内容的提问来源于stack exchange,提问作者user112167
相关产品推荐
相关产品推荐

