基于ACM Graphics(Java)的吃豆人碰撞检测异常问题排查
帮你搞定吃豆人碰撞问题~
嘿,我看了你写的两段代码,问题的核心其实都是碰撞检测的时机和碰撞后的处理逻辑不对,导致吃豆人要么穿砖块,要么卡里面。我来给你拆解下问题,再给你修正方案:
第一个版本的问题
你先让吃豆人完成移动,再检测碰撞,这时候吃豆人已经钻进砖块里了,再调用pac.move(0,0)根本没用——因为这个方法只是让它不移动,但已经穿进去的位置不会变。而且你的碰撞方向判断逻辑也没起到实际作用,就算判断对了方向,也没有把吃豆人移回砖块外面。
先贴下你原来的代码方便对照:
private void gameLoop() { boolean done = false; while (!done) { if(pacmanState == "right") { pac.move(20, 0); pause(100); } if(pacmanState == "left") { pac.move(-20, 0); pause(100); } if(pacmanState == "down") { pac.move(0, 20); pause(100); } if(pacmanState == "up") { pac.move(0, -20); pause(100); } if(pac.getBounds().intersects(b2.getBounds())) { if(pac.getY() <= b2.getY() - (b2.getHeight()/2)) { //Hit was from below the brick pac.move(0, 0); } if(pac.getY() >= b2.getY()+ (b2.getHeight()/2)) { //Hit was from above the brick pac.move(0, 0); } if(pac.getX() < b2.getX()) { //Hit was on left pac.move(0, 0); } if(pac.getX() > b2.getX()){ //Hit was on right pac.move(0, 0); } } } }
第二个switch版本的问题
你把移动和碰撞检测放switch里了,但逻辑还是乱的:先移动,然后如果碰撞了要么原地不动,要么又移动一次,这会导致吃豆人卡在砖块中间——因为移动后已经穿进去了,没有回退到碰撞前的位置,自然移不开。
你的switch代码片段:
switch (pacmanState) { case "left": pac.move(-20, 0); pause(100); if (pac.getBounds().intersects(b2.getBounds())) { if (pac.getX() > b2.getX()) { pac.move(0, 0); } else { pac.move(-20, 0); pause(100); } } break; }
修正方案:先记录位置,碰撞就回退
最直接有效的方法是:先保存吃豆人移动前的位置,尝试移动后检测碰撞,如果碰撞了就把它移回移动前的位置。这样既不会穿砖块,也不会卡里面。
修改后的代码示例:
private void gameLoop() { boolean done = false; while (!done) { // 第一步:记录移动前的坐标 int prevX = pac.getX(); int prevY = pac.getY(); // 第二步:根据方向执行移动 switch(pacmanState) { case "right": pac.move(20, 0); break; case "left": pac.move(-20, 0); break; case "down": pac.move(0, 20); break; case "up": pac.move(0, -20); break; default: // 没有方向时不移动 break; } pause(100); // 第三步:检测碰撞,碰撞就回退到之前的位置 if(pac.getBounds().intersects(b2.getBounds())) { pac.setLocation(prevX, prevY); } } }
如果你需要更精确的方向碰撞(比如只回退X或Y轴,而不是整个位置),可以再优化:比如判断碰撞时X轴重叠还是Y轴重叠,只回退对应的轴。比如如果是左右方向碰撞,就只回退X坐标,Y坐标保持不变,这样体验更自然。
举个方向优化的例子:
if(pac.getBounds().intersects(b2.getBounds())) { // 计算X和Y方向的重叠量 int overlapX = Math.min(pac.getX() + pac.getWidth(), b2.getX() + b2.getWidth()) - Math.max(pac.getX(), b2.getX()); int overlapY = Math.min(pac.getY() + pac.getHeight(), b2.getY() + b2.getHeight()) - Math.max(pac.getY(), b2.getY()); // 重叠量小的方向是碰撞的主要方向,只回退这个方向 if(overlapX < overlapY) { // 左右方向碰撞,回退X坐标 if(pac.getX() < b2.getX()) { pac.setLocation(b2.getX() - pac.getWidth(), pac.getY()); } else { pac.setLocation(b2.getX() + b2.getWidth(), pac.getY()); } } else { // 上下方向碰撞,回退Y坐标 if(pac.getY() < b2.getY()) { pac.setLocation(pac.getX(), b2.getY() - pac.getHeight()); } else { pac.setLocation(pac.getX(), b2.getY() + b2.getHeight()); } } }
这样处理后,吃豆人就会刚好停在砖块的边缘,不会穿进去也不会卡住啦~
内容的提问来源于stack exchange,提问作者zver.oxuyan.ushaq.2
相关产品推荐
相关产品推荐

