圆形与正方形碰撞检测:如何获取碰撞接触位置实现游戏防穿透?
游戏场景下圆与矩形碰撞检测的可落地改造方案
该类通用函数仅返回布尔碰撞结果,缺失游戏阻挡逻辑必需的两个关键信息:矩形与圆形的实际接触点位置、将圆形移出碰撞重叠区所需的最小位移向量。原有函数的几何判断逻辑本身完全正确,不需要推翻重写,只需要把判断分支的输出从简单的true/false,替换为结构化的碰撞细节即可。
核心逻辑优化思路
- 无需在原有坐标轴差计算的基础上额外叠加复杂几何计算,只需要先定位矩形边界上距离圆心最近的点,所有碰撞判断、位置计算都基于这个最近点推导
- 所有快速排除无碰撞的判断逻辑和原函数完全一致,仅当确认发生碰撞时,才计算需要返回的位置、方向、距离信息,保证运行性能
- 覆盖圆心完全落入矩形内部的边界场景,避免角落判断逻辑出现除零错误
分场景计算规则
- 边碰撞场景:圆心垂直方向落在矩形上下边界范围内,水平方向与矩形左/右边界的距离小于圆半径,此时接触点落在矩形的垂直边上,推出方向为水平方向
- 边碰撞场景:圆心水平方向落在矩形左右边界范围内,垂直方向与矩形上/下边界的距离小于圆半径,此时接触点落在矩形的水平边上,推出方向为垂直方向
- 角碰撞场景:圆心既不在矩形水平投影范围内,也不在垂直投影范围内,此时接触点为矩形的对应角点,推出方向为从角点指向圆心的连线方向
改造后可直接复用的检测函数
function rccol(rect, circle){ // 拆解矩形四周边界坐标 const rectLeft = rect.x; const rectRight = rect.x + rect.width; const rectTop = rect.y; const rectBottom = rect.y + rect.height; // 定位矩形上距离圆心最近的点 const closestX = Math.max(rectLeft, Math.min(circle.x, rectRight)); const closestY = Math.max(rectTop, Math.min(circle.y, rectBottom)); // 计算圆心到最近点的距离平方,快速判断是否碰撞 const dx = circle.x - closestX; const dy = circle.y - closestY; const distSq = dx * dx + dy * dy; // 距离大于圆半径,无碰撞 if(distSq > circle.radius * circle.radius) return null; // 处理碰撞逻辑,计算需要返回的全部参数 const dist = Math.sqrt(distSq); let normalX, normalY, penetration; // 兜底:圆心完全在矩形内部时的推出方向计算 if(dist === 0) { const dLeft = circle.x - rectLeft; const dRight = rectRight - circle.x; const dTop = circle.y - rectTop; const dBottom = rectBottom - circle.y; const minSideDist = Math.min(dLeft, dRight, dTop, dBottom); penetration = circle.radius + minSideDist; if(minSideDist === dLeft) {normalX = -1; normalY = 0;} else if(minSideDist === dRight) {normalX = 1; normalY = 0;} else if(minSideDist === dTop) {normalX = 0; normalY = -1;} else {normalX = 0; normalY = 1;} } else { normalX = dx / dist; normalY = dy / dist; penetration = circle.radius - dist; } return { contactPoint: {x: closestX, y: closestY}, pushOutVector: {x: normalX * penetration, y: normalY * penetration}, normal: {x: normalX, y: normalY} }; }
游戏开发中的使用提示
- 每帧玩家移动完成后调用该检测函数,拿到返回的
pushOutVector后直接叠加到玩家碰撞盒的坐标上,即可实现墙体阻挡、不穿透的效果 - 检测全程优先用平方距离做无碰撞快速排除,仅在确认发生碰撞时计算平方根,可大幅降低高频检测的性能消耗
- 如果需要做碰撞反馈(比如粒子效果、音效触发),可以直接使用返回的
contactPoint坐标定位触发位置,不需要额外做坐标换算
内容的提问来源于stack exchange,提问作者kriskotoo BG
相关产品推荐
相关产品推荐

