Java顶视角2D游戏碰撞检测Bug求助:速度翻倍、数组越界
问题汇总
- 玩家向下移动时速度翻倍,直接超出5x5测试地图边界,随后游戏卡顿、玩家无法移动
- 控制台抛出数组越界异常:
Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: Index 5 of 5 out of bounds for length 5
at lu.embellishedduck.liminalmurmurs.engine.CollisionDetection.checkTileCollision(CollisionDetection.java:134) - 单一方向碰撞墙体后,尝试斜向移动会被推入墙体约4像素并卡住
核心Bug根源分析
1. 重复移动导致速度翻倍(Player类)
在Player.update()方法的主移动分支中,向下移动被执行了两次位移操作:
if (keyHandler.isDownPressed()) { setDirection("down"); worldY += getMovementSpeed(); // 第一次位移 }
之后在无碰撞校验通过的switch分支中,又执行了第二次位移:
case "down" : worldY += getMovementSpeed(); // 第二次位移 break;
双重位移直接导致向下速度翻倍,玩家瞬间冲出地图边界,触发数组越界。
2. 无边界校验引发数组越界(CollisionDetection类)
碰撞检测时未对计算出的行列索引做合法性校验,当玩家超出地图后,entityBottomRow会等于5(5x5地图有效行索引为0-4),直接访问mapTileNum[entityLeftCol][5]触发数组越界,进而导致游戏逻辑卡顿。
3. 斜向碰撞检测覆盖不全
斜向移动的碰撞检测仅检查单个角落的Tile,未覆盖移动路径上的所有可能碰撞点,导致碰撞处理不精准,出现被推入墙体的情况。
修复方案
1. 修复重复移动问题
修改Player.update()方法中向下移动的代码,移除多余的位移操作:
// 原代码 if (keyHandler.isDownPressed()) { setDirection("down"); worldY += getMovementSpeed(); // 删除此行 }
2. 添加碰撞检测边界校验
在CollisionDetection.checkTileCollision()方法中,所有访问mapTileNum数组的操作前,添加索引合法性校验。以向下移动分支为例:
case "down" : entityBottomRow = (entityBottomWorldY + entity.getMovementSpeed()) / gamePanel.getTILE_SIZE(); // 添加边界校验:超出地图范围直接标记碰撞 int mapColCount = gamePanel.getTileManager().getMapTileNum().length; int mapRowCount = gamePanel.getTileManager().getMapTileNum()[0].length; if (entityBottomRow >= mapRowCount || entityBottomRow < 0 || entityLeftCol >= mapColCount || entityLeftCol < 0 || entityRightCol >= mapColCount || entityRightCol < 0) { entity.setCollisionOn(true); break; } tileNum1 = gamePanel.getTileManager().getMapTileNum()[entityLeftCol][entityBottomRow]; tileNum2 = gamePanel.getTileManager().getMapTileNum()[entityRightCol][entityBottomRow]; if (gamePanel.getTileManager().getTiles()[tileNum1].isCollision() || gamePanel.getTileManager().getTiles()[tileNum2].isCollision()) { entity.setCollisionOn(true); } break;
其他方向(up、left、right及所有斜向)需添加类似的边界校验逻辑。
3. 优化斜向碰撞检测覆盖范围
以up-left方向为例,修改碰撞检测逻辑,覆盖移动路径上的多个Tile:
case "up-left" : int predictedTopRow = (entityTopWorldY - entity.getMovementSpeed()) / gamePanel.getTILE_SIZE(); int predictedLeftCol = (entityLeftWorldX - entity.getMovementSpeed()) / gamePanel.getTILE_SIZE(); // 边界校验 int mapColCount = gamePanel.getTileManager().getMapTileNum().length; int mapRowCount = gamePanel.getTileManager().getMapTileNum()[0].length; if (predictedTopRow < 0 || predictedLeftCol < 0 || predictedLeftCol >= mapColCount || predictedTopRow >= mapRowCount) { entity.setCollisionOn(true); break; } // 检查左上角、左列当前行、上列当前列的Tile int tileNum1 = gamePanel.getTileManager().getMapTileNum()[predictedLeftCol][predictedTopRow]; int tileNum2 = gamePanel.getTileManager().getMapTileNum()[predictedLeftCol][entityTopRow]; int tileNum3 = gamePanel.getTileManager().getMapTileNum()[entityLeftCol][predictedTopRow]; if (gamePanel.getTileManager().getTiles()[tileNum1].isCollision() || gamePanel.getTileManager().getTiles()[tileNum2].isCollision() || gamePanel.getTileManager().getTiles()[tileNum3].isCollision()) { entity.setCollisionOn(true); } break;
其他斜向方向(up-right、down-left、down-right)需按照此逻辑扩展碰撞检测覆盖范围。
验证步骤
- 运行游戏,测试向下移动,确认速度恢复正常,不会超出地图边界
- 测试所有8个方向的移动,包括碰撞墙体后的斜向移动,确认不再出现卡顿或被推入墙体的情况
- 查看控制台,确认数组越界异常不再抛出
内容的提问来源于stack exchange,提问作者Will Blanchard

