碰撞检测的基础内容很多,这里仅挑一些常用的和经典的内容进行说明。
本文作者游蓝海。原创不易,未经许可,禁止任何形式的转载。
1. 常用的向量知识
1.1 向量点乘
向量点乘常用于计算向量之间的投影长度和夹角。如果夹角小于90度,则点乘结果为正数;大于90度则为负数。
a ⋅ b = ∣ a ∣ ∣ b ∣ c o s ( θ ) = a x b x + a y b y a \cdot b = |a||b|cos(\theta) = a_x b_x + a_y b_y a⋅b=∣a∣∣b∣cos(θ)=axbx+ayby
1.2 向量叉乘
2D向量叉乘常用于计算向量之间的旋转角度,可用于方位判定。如果a到b的旋转角度小于180度,则叉乘结果为正数;否则为负数。
a × b = ∣ a ∣ ∣ b ∣ s i n ( θ ) = a x b y − a y b x a \times b = |a||b|sin(\theta) = a_x b_y - a_y b_x a×b=∣a∣∣b∣sin(θ)=axby−aybx
2. 点的位置判定
2.1 点到直线的最近距离
先计算出点p在直线上的投影点,也就是过点p的垂线在直线上的垂足q,然后计算点到垂足的距离。设直线的方程为: p = p 0 + n ⃗ t p=p_0+\vec{n}t p=p0+nt,其中n为直线的方向,t为任意长度。则垂足q的计算公式为:
t = ( p − p 0 ) ⋅ n ∣ n ∣ 2 q = p 0 + n ⃗ t t = \frac {(p - p_0) \cdot n} {|n|^2} \\ q = p_0 + \vec{n} t t=∣n∣2(p−p0)⋅nq=p0+nt
这里需要注意一个技巧,本来计算 p 0 p ⃗ \vec{p_0p} p0p在n上的投影长度t时,n向量应当单位化,也就是要除以n的长度。将t再次除以n的长度,就可以将t格式化[0, 1]之间。这样既可以方便的范围判定,也避免了求长度的开方运算。
2.2 点到线段最近距离
点到线段的最近距离点可能是垂足,也可能是线段两个端点的其中之一。设线段起点为p0,终点为p1,则线段的方程为:
p = p 0 + n ⃗ t n ⃗ = p 1 − p 0 t ∈ [ 0 , 1 ] p=p_0 + \vec{n}t \\ \vec{n}=p_1 - p_0 \\ t \in [0, 1] p=p0+ntn=p1−p0t∈[0,1]
代入2.1节中的公式计算出t:
- 如果t < 0,则最近距离点就是p0
- 如果t > 1,则最近距离点就是p1
- 其余情况下,最近距离点就是垂足: q = p 0 + n ⃗ t q = p_0 + \vec{n} t q=p0+nt
2.3 点与直线的方位判定
判读点在直线的哪一侧,可以用叉乘。
t = ( p − p 0 ) × n ⃗ t = (p - p0) \times \vec n t=(p−p0)×n
如果t < 0,在左侧;t > 0在右侧;t = 0,在直线上。
2.4 点和圆的位置关系
设圆的方程为: p = p 0 + n ⃗ r p = p_0 + \vec{n} r p=p0