谁说数学没用——基元碰撞检测(2)

 这里介绍的是“基元”之间的碰撞检测,所谓“基元”就是线段、三角形、矩形、平面、圆、椭圆等各种常见的、能用一两个数学公式表示的图形。“基元碰撞检测”是游戏开发中常用的手段,用数学公式求解碰撞结果,能让我们系统性的理解其中的原理。大家也不用担心,里面用到的数学公式,充其量高中、大一都学过,都属于“空间解析几何”范畴。


-----------------------------------------------   华丽的分割线   -----------------------------------------------


        前面讲了2D线段”与“2D线段”之间的碰撞检测,这次是“2D线段”与“2D圆”的碰撞检测。

先说结果

        1. 两者如果有正常的“相交”,那么结果是1~2个交点,我们只取“离线段起始点最近的”那个点。

        2. 如果两者相切,就是两个交点在同一个位置,随便取一个即可。

        3. 在以下情况下认为没有交点:

                   圆心到线段的距离 > 圆的半径

                   线段很短、完全在圆内

                   线段与圆相交在延长线上

                  如果线段的两个端点重合,即线段是一个点,也认为没有交点。


开始推导:

假设在2D坐标系中,有一个圆,如下图所示:

        圆心为C,坐标为(Xc, Yc),半径为R

                                 

那么,对于圆周上任意一点P,到C的距离为R,两点之间的距离可以用如下公式计算: 

                                     d = sqrt( (Xp - Xc)^2 +(Yp - Yc)^2) ) = R

用向量可以表示为:

                                    d = | P - C | = R


对于线段P1 -> Q1,前面讲过,对于该线段上任意一个点P,可以用如下“参数方程”表示:

                                    P = P1 + t * V1        t ∈ [0, 1] 

                                  


-----------------------------------------------   华丽的分割线   -----------------------------------------------

有了以上基础,我们就可以开始计算两者之间的碰撞点了。

假设两根线段相交于点P,那么联立方程组:

                                    | P - C | = R

                                    P = P1 + t * V1        t ∈ [0, 1] 

                                

把P代进圆的方程:

                                    | P1 + t * V1 - C | = R

                                    | t * V1 + (P1 - C) | = R

                                    | t * V1 + CP1 | = R

注意这里左边的“V1”、“CP1”都是向量,右边的R是标量。

两边平方去掉绝对值符号:

                                   (t * V1 + CP1)^2 = R^2

                                    V1^2 * t^2 + (2 * CP1 * V1) * t + CP1^2 = R^2

                                    V1^2 * t^2 + (2 * CP1 * V1) * t + (CP1^2 - R^2) = 0

这里,向量的平方就是对自己点积,向量的乘法就是点积,我们做进一步计算:

                                    V1^2 = V1 * V1 = dot_product(V1, V1) = a

                                    2 * CP1 * V1 = 2 * dot_product(CP1, V1) = b

                                    CP1^2 - R^2CP1 * CP1 - R^2 = dot_product(CP1, CP1) - R^2 = c

我们可以把方程简化为:

                                    a * t^2 + b * t + c = 0


这个方程,大家有没有很熟悉,初中里都学过,一元二次方程

它有一个专门的求根公式:

                                   

其中,

      当 Δ >= 0的时候,可以开根号,能求得两个“实数解”,线段与圆有1~2个交点。

      当 Δ < 0的时候,这种情况下,其实是“圆心到线段的距离”大于圆的半径,故不会相交。


求根公式图片是从baidu上找的,我们把 x 替换成 t,就能得到两个解:

                                    t1 = ( -b - sqrt( b^2 - 4ac ) ) / 2a

                                    t2 = ( -b + sqrt( b^2 - 4ac ) ) / 2a

可以看到t1 <= t2。

至此,我们根据t1、t2的值就可以判断,线段与圆是否有效相交:

      t1、t2都在[0, 1]范围内时,即 0 <= t1 <= t2 <= 1,这两个都是有效解,相交于两个点。

      如果只有一个在[0, 1]范围内,则那个是有效解,即只相交一个点。

      如果t1 = t2,则线段与圆相切,线段所在直线是圆的切线,切点就是相交点,正好在圆周上。


-----------------------------------------------   华丽的分割线   -----------------------------------------------

原理讲完了,求解部分的伪代码如下:

           if (a == 0)     return No Intersection;             // 线段两个端点重合


           Δ = b^2 - 4ac

           if  (Δ < 0 )     return No Intersection;


           t1 = ( -b - sqrt(Δ) ) / 2a

           if ( t1 >= 0 && t1 <= 1 )    

                  P = P1 + t1 * V1

                  return P;

           t2 = ( -b + sqrt(Δ) ) / 2a

           if ( t2 >= 0 && t2 <= 1 )    

                  P = P1 + t2 * V1

                  return P;


          return No Intersection;


特别需要注意的是,如果V1的长度为0,线段两个端点重合,即a == 0,要特殊处理。

各种相交情况可以看以下这些图:




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值