使用SAT检测圆形与多边形碰撞时靠近多边形角落判定失效如何解决
问题原因
你当前的SAT实现仅检测了多边形所有边的法向量作为分离轴,而多边形与圆形的SAT碰撞检测规则要求必须额外增加一类分离轴:多边形上距离圆心最近的顶点与圆心连线的单位向量,这类轴是应对圆形靠近多边形顶点的碰撞场景的,漏测这类轴就会导致角落位置的碰撞判定异常。
修复方案
在你现有遍历所有边法向量的检测逻辑结束后,新增以下步骤:
- 遍历多边形所有顶点,计算每个顶点与圆心的距离,找到距离最近的顶点
- 生成从该最近顶点指向圆心的向量,归一化后作为额外分离轴
- 对该额外轴执行和边法向量轴完全相同的投影、重叠检测逻辑,计算重叠量并更新最小平移向量(MTV)
- 若该轴上两个形状的投影无重叠,直接返回不碰撞
代码修改示例
你可以在现有for (int i = 0; i < array_len(poly->coords); i++)循环结束后,补充以下代码:
// 新增:查找多边形最近顶点,生成额外分离轴 double min_dist_sq = DBL_MAX; AS_Vector *nearest_vertex = NULL; for (int i = 0; i < array_len(poly->coords); i++) { AS_Vector *v = poly->coords[i]; double dx = v->x - circle->position->x; double dy = v->y - circle->position->y; double dist_sq = dx*dx + dy*dy; if (dist_sq < min_dist_sq) { min_dist_sq = dist_sq; nearest_vertex = v; } } // 生成额外轴:最近顶点指向圆心的单位向量 axis_normed->x = circle->position->x - nearest_vertex->x; axis_normed->y = circle->position->y - nearest_vertex->y; // 处理圆心和顶点重合的特殊情况 if (axis_normed->x == 0 && axis_normed->y == 0) { axis_normed->x = 1; axis_normed->y = 0; } AS_vector_norm(axis_normed); // 对该额外轴执行和边轴完全相同的投影、重叠检测逻辑 double min1 = DBL_MAX, max1 = -DBL_MAX; double dot_product; for (int j = 0; j < array_len(poly->coords); j++) { dot_product = AS_vector_dot(poly->coords[j], axis_normed); if (dot_product > max1) max1 = dot_product; if (dot_product < min1) min1 = dot_product; } double b = AS_vector_dot(circle->position, axis_normed); double min2 = b - circle->radius; double max2 = b + circle->radius; if (result) { double overlap; if (min1 < min2) { result->a_in_b = AS_FALSE; if (max1 < min2) { result->is_overlapping = AS_FALSE; return AS_FALSE; } else if (max1 < max2) { overlap = max1 - min2; } else { double option1 = max1 - min2; double option2 = max2 - min1; overlap = option1 < option2 ? option1 : option2; } } else { result->b_in_a = AS_FALSE; if (max2 < min1) { result->is_overlapping = AS_FALSE; return AS_FALSE; } else if (max2 < max1) { overlap = max2 - min1; } else { double option1 = max2 - min1; double option2 = max1 - min2; overlap = option1 < option2 ? option1 : option2; } } if (overlap < result->overlap) { result->overlap = overlap; result->overlap_x = axis_normed->x; result->overlap_y = axis_normed->y; } } else { if (min1 > max2 || min2 > max1) { return AS_FALSE; } }
其他优化点
你现有代码中计算圆形投影区间的逻辑冗余:b - radius天然就是最小投影值,b + radius天然就是最大投影值,不需要多次比较赋值,可以直接简化为min2 = b - circle->radius; max2 = b + circle->radius;。
内容的提问来源于stack exchange,提问作者user17232631
相关产品推荐
相关产品推荐

