基于勾股定理的2D正方形与矩形碰撞检测及物理响应实现求助
问题根因分析
- 原逻辑基于矩形边中点距离判断碰撞方向,仅对宽高接近的正方形有效。当矩形宽高比例差异大时(比如你代码里宽100高30的平台),边中点距离无法正确反映真实碰撞方向,甚至会出现已经碰撞但中点距离还很大的误判
- 碰撞响应逻辑错误:静态平台不需要修改位置,原代码同时调整了玩家和平台的坐标,导致碰撞后平台移位、玩家位置异常
- 执行顺序错误:原代码先做碰撞检测再更新玩家位置,碰撞检测用的是上一帧的玩家坐标,容易出现错位
- HTML样式错误:
body的宽度设置为100vh,应该为100vw,否则画布会出现横向滚动条
修复方案
- 修正HTML样式
body { height:100vh; width:100vw; /* 把原来的100vh改成100vw */ margin: 0; overflow: hidden; /* 新增,隐藏页面滚动条 */ }
- 替换碰撞检测逻辑,先做AABB预过滤,再用重叠深度判断碰撞方向,适配任意比例矩形:
function platformDetection(obj, obj2){ // 第一步:AABB碰撞预检测,没碰撞直接返回,节省性能也避免误判 if ( obj.x + obj.w < obj2.x || obj.x > obj2.x + obj2.w || obj.y + obj.h < obj2.y || obj.y > obj2.y + obj2.h ) return; // 第二步:计算两个矩形的中心坐标差 const objCenterX = obj.x + obj.w / 2; const objCenterY = obj.y + obj.h / 2; const obj2CenterX = obj2.x + obj2.w / 2; const obj2CenterY = obj2.y + obj2.h / 2; const dx = objCenterX - obj2CenterX; const dy = objCenterY - obj2CenterY; // 第三步:计算x、y轴的重叠深度,重叠更小的轴就是碰撞方向 const overlapX = (obj.w / 2 + obj2.w / 2) - Math.abs(dx); const overlapY = (obj.h / 2 + obj2.h / 2) - Math.abs(dy); let val = 0; if (overlapX < overlapY) { // X轴碰撞 val = dx > 0 ? 4 : 2; // 4=玩家左撞平台右,2=玩家右撞平台左 } else { // Y轴碰撞 val = dy > 0 ? 1 : 3; // 1=玩家顶撞平台底,3=玩家底撞平台顶 } // 可以保留你原来的辅助线绘制逻辑,对应val的值绘制即可 platformAction(obj, obj2, val); }
- 修正碰撞响应逻辑,仅修改玩家坐标,不要改动静态平台的位置:
function platformAction(obj, obj2, val){ if (val == 1) { // 玩家顶部撞平台底部 obj.y = obj2.y + obj2.h; obj.vy = 0; obj.jumping = true; } else if (val == 2) { // 玩家右边撞平台左边 obj.x = obj2.x - obj.w - 1; obj.vx = 0; } else if (val == 3) { // 玩家底部撞平台顶部 obj.y = obj2.y - obj.h; obj.vy = 0; obj.jumping = false; } else if (val == 4) { // 玩家左边撞平台右边 obj.x = obj2.x + obj2.w + 1; obj.vx = 0; } }
- 调整循环执行顺序,先更新玩家位置再做碰撞检测:
function loop() { context.clearRect(0, 0, canvas.width, canvas.height); context.fillStyle = 'grey'; context.fillRect(0, 0, canvas.width, canvas.height); // 更新平台 initObj(platform1); initObj(platform2); // 先更新玩家位置 controlPlayer1(player1); // 再执行碰撞检测 platformDetection(player1, platform1); platformDetection(player1, platform2); requestAnimationFrame(loop) }
该方案继承了你原来的“单方向判定、避免多if同时命中”的设计思路,且适配任意比例的矩形碰撞,不需要额外的多条件分支判断。
内容的提问来源于stack exchange,提问作者MPdoor2
相关产品推荐
相关产品推荐

