You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 11:54:10