讲师中心 微信公众号
AI工具推荐 视频效率加速

基于分离轴定理(SAT)的圆与多边形碰撞检测完整实现与修正指南

冬萱吖_7723

冬萱吖_7723

发布时间:2026-10-08 20:37:09

|

741人浏览过

|

来源于php中文网

原创

基于分离轴定理(SAT)的圆与多边形碰撞检测完整实现与修正指南

本文详解如何正确实现圆与凸多边形的sat碰撞检测,重点修复原始代码中因投影重叠判断逻辑缺陷导致的垂直方向误判问题,并提供可直接运行的健壮实现。

本文详解如何正确实现圆与凸多边形的sat碰撞检测,重点修复原始代码中因投影重叠判断逻辑缺陷导致的垂直方向误判问题,并提供可直接运行的健壮实现。

在游戏开发(如高尔夫模拟)中,精确、高效的碰撞检测至关重要。虽然分离轴定理(SAT)常用于凸多边形–凸多边形检测,但将其扩展至圆–凸多边形时需特别注意:圆可视为无限条边的正多边形,其“支持方向”并非固定边法向,而是从圆心指向多边形顶点或边的最近点方向。然而,一种更简洁且等效的常用策略是——将圆“膨胀”为以圆心为中心、半径为 r 的点集,并将多边形沿所有边法向“收缩”相同距离(即Minkowski差思想)。此时,原问题等价于判断该点是否落在“收缩后的多边形”内部。

但原始代码采用了一种简化思路:对多边形每条边计算其法向量,将圆心 E 沿该法向平移 ±r 得到两个端点 F 和 G(代表圆在该轴上的投影区间),再与边端点 A、B 在同一轴上的投影比较是否重叠。该思路本身合理,核心缺陷在于 check_overlap 函数:它错误地认为只要 x 或 y 坐标满足区间包含即可判定投影重叠,而实际上——投影重叠必须在该一维轴上成立,即应直接比较标量投影值,而非二维坐标分量。

原始 check_overlap 的逻辑混淆了“二维线段在坐标轴上的投影”与“一维投影轴上的区间重叠”。正确做法是:对每条边法向 n,我们已计算出四个标量投影值:

  • a = A · n(顶点 A 在法向 n 上的投影)
  • b = B · n(顶点 B 在法向 n 上的投影)
  • g = G · n = (E − r·n) · n = E·n − r(圆在负方向的投影端点)
  • f = F · n = (E + r·n) · n = E·n + r(圆在正方向的投影端点)

因此,该轴上的多边形投影区间为 [min(a,b), max(a,b)],圆的投影区间为 [E·n − r, E·n + r]。二者重叠当且仅当:

not (max(a, b) < e - r or min(a, b) > e + r)
# 等价于:
e - r <= max(a, b) and min(a, b) <= e + r

据此,我们重构关键函数如下(已修复、注释清晰、兼容凸多边形):

import math

def get_edge_normal(v1, v2):
    """返回边 v1->v2 的单位法向量(指向多边形外部,需确保顶点逆时针序)"""
    dx, dy = v2[0] - v1[0], v2[1] - v1[1]
    nx, ny = -dy, dx  # 逆时针旋转90°得外法向
    norm = math.sqrt(nx*nx + ny*ny)
    if norm == 0:
        return [0.0, 0.0]
    return [nx / norm, ny / norm]

def project_point_on_axis(point, axis):
    """计算点在轴(单位向量)上的标量投影值"""
    return point[0] * axis[0] + point[1] * axis[1]

def intervals_overlap(min1, max1, min2, max2):
    """判断一维区间 [min1, max1] 与 [min2, max2] 是否重叠"""
    return max1 >= min2 and max2 >= min1

def circle_polygon_collision(vertices, center, radius):
    """
    使用SAT检测圆与凸多边形碰撞(顶点须为逆时针顺序)
    :param vertices: 多边形顶点列表,如 [(x0,y0), (x1,y1), ...]
    :param center: 圆心坐标 (cx, cy)
    :param radius: 圆半径
    :return: bool,True 表示发生碰撞
    """
    if len(vertices) < 3:
        return False

    # 检查所有边法向(多边形轴)
    for i in range(len(vertices)):
        v1 = vertices[i]
        v2 = vertices[(i + 1) % len(vertices)]
        normal = get_edge_normal(v1, v2)

        # 投影多边形边端点
        proj_v1 = project_point_on_axis(v1, normal)
        proj_v2 = project_point_on_axis(v2, normal)
        poly_min = min(proj_v1, proj_v2)
        poly_max = max(proj_v1, proj_v2)

        # 投影圆心并计算圆在该轴的投影区间
        proj_center = project_point_on_axis(center, normal)
        circle_min = proj_center - radius
        circle_max = proj_center + radius

        # 若在任一轴上无重叠,则分离,无碰撞
        if not intervals_overlap(poly_min, poly_max, circle_min, circle_max):
            return False

    # 所有轴均重叠 → 发生碰撞
    return True

# ✅ 使用示例:
if __name__ == "__main__":
    # 逆时针三角形(地面障碍)
    tri = [(100, 100), (200, 80), (150, 200)]
    # 圆(高尔夫球)
    ball_center = (140, 90)
    ball_radius = 15

    collide = circle_polygon_collision(tri, ball_center, ball_radius)
    print("Collision:", collide)  # True(应碰撞)

关键注意事项与总结:

  • 顶点顺序至关重要:get_edge_normal 假设顶点为逆时针(CCW)排列,以保证法向指向多边形外部。若为顺时针,需将 nx, ny = dy, -dx;或统一用叉积符号校验。
  • 凸性前提:SAT 仅对凸多边形有效。对凹多边形需先三角剖分或使用其他算法(如GJK)。
  • 性能优化:实际项目中可预计算并缓存边法向;对大量对象,建议结合空间分区(如四叉树)减少检测对数。
  • 数值稳定性:避免除零(已处理)、使用 math.isclose() 替代 == 比较浮点数(本例中非必需但推荐)。
  • 调试技巧:可视化每条边的法向及投影区间,是定位SAT逻辑错误最有效的方式。

此实现彻底规避了原始代码中 check_overlap 对二维坐标分量的错误依赖,严格遵循SAT在一维投影轴上判断重叠的核心原则,确保在任意朝向(水平、垂直、倾斜)下均能给出精确结果。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

相关专题

更多
python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

1671

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

4204

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

1669

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

24417

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2987

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

3007

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

1163

2023.08.11

python合并两个列表
python合并两个列表

Python是一种强大的编程语言,具有许多方便的功能和工具。在Python中,有多种方法可以合并两个列表。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

596

2023.08.10

FrankenPHP集成Laravel详细教程
FrankenPHP集成Laravel详细教程

本专题提供FrankenPHP集成Laravel的详细配置指南,全面解析运行原理、开发环境搭建、Caddyfile配置、Octane工作模式、数据库连接、队列任务、定时任务和生产环境优化,解决部署过程中常见的报错与兼容性问题。

0

2026.10.08

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn