在游戏开发中,我们一般常用的碰撞检测算法有AABB,OBB以及分离轴算法。
AABB与OBB一般用于矩形的碰撞检测,而多边形的碰撞检测一般使用分离轴算法,这里说的都是2D图形的碰撞检测
一、什么是分离轴?
将两个多边形投影到任意的一条轴上,如果能找到至少有一个轴上的两个多边形投影没有重叠,那么就说明这两个多边形没有发生碰撞。
在程序实现中,我们不可能去检测所有的轴,这是不现实的,不过好在多边形的特性,我们只需要以多边形的每一条边的法线为轴,分别检测一遍就好了。
分离轴适用的是凸多边形之间检测,不适用于凹多边形,凹多边形的检测,可以通过算法将凹多边形分割成多个凸多边形再进行计算。

分离轴.png
多边形A,B在轴Z''上的投影A''与B''是分离的,所以多边形A,B是分离的。
二、算法实现
2.1、多边形与多边形
(1)辅助方法
---获取两点的距离
function GetDis(vec1,vec2)
local x1 = vec1.x or vec1[1]
local y1 = vec1.y or vec1[2]
local x2 = vec2.x or vec2[1]
local y2 = vec2.y or vec2[2]
local disX = x1 - x2
local disY = y1 - y2
local dis = math.sqrt(disX * disX + disY * disY)
return dis
end
---向量归一化
function Normalize(vec)
local x = vec[1] or vec.x
local y = vec[2] or vec.y
local mag = math.sqrt(x*x + y*y)
if type(vec) == "table" then
vec[1] = x/mag
vec[2] = y/mag
end
vec.x = x/mag
vec.y = y/mag
end
---点乘
function Dot(vec1,vec2)
local x1 = vec1.x or vec1[1]
local y1 = vec1.y or vec1[2]
local x2 = vec2.x or vec2[1]
local y2 = vec2.y or vec2[2]
return x1*x2 + y1*y2
end
---精确到小数点后n位
---num 浮点数
---n 浮点数精确位数
function FloatAccurateN(num,n)
if type(num) ~= "number" then
return num;
end
n = n or 0;
n = math.floor(n)
if n < 0 then
n = 0;
end
local nDecimal = 10 ^ n
local nTemp = math.floor(num * nDecimal);
local nRet = nTemp / nDecimal;
return nRet;
end
---二维向量的向量积
---大小的绝对值表示两个向量构成的三角形的面积的2倍
---正负表示与两个向量构成的平面的法线的方向
function VectorProduct(vec1,vec2)
local vec1X = vec1.x or vec1[1]
local vec1Y = vec1.y or vec1[2]
local vec2X = vec2.x or vec2[1]
local vec2Y = vec2.y or vec2[2]
return vec1X*vec2Y - vec2X*vec1Y
end
function Add(pt1,pt2)
return {x = pt1.x + pt2.x , y = pt1.y + pt2.y }
end
function Sub(pt1,pt2)
return {x = pt1.x - pt2.x , y = pt1.y - pt2.y }
end
-- 计算圆周上的点位置
-- 返回的是通过圆心与水平夹角为angleRadians度的直径在圆上的两个交点
function CalCirclePos(centerPos, radius, angleRadians)
return Add(centerPos, {x=math.cos(angleRadians)*radius, y=math.sin(angleRadians)*radius}),Sub(centerPos, {x=math.cos(angleRadians)*radius, y=math.sin(angleRadians)*radius})
end
---将角度转换为逆时针角度
---rotation (0 ~ 180逆时针,0 ~ -180顺时针)
function changeRotationToInverse(rotation)
rotation = rotation or 0
if rotation < 0 then
rotation = rotation + 360
end
return rotation or 0
end
(2)创建多边形
--创建一个多边形
--name:多边形名字
--offset:实际点与多边形的偏移量,offset为多边形的原点,多边形的顶点位置都是相对于这个点的偏移量
--vertices : 多边形的顶点数组,位置相对于offset
--rotation : 旋转角度(角度不是弧度(0~180为逆时针,0~-180为顺时针)
function Polyon(name,offset,vertices,rotation)
local polyon = {}
polyon.name = name or "polyon"
polyon.offset = {offset.x or offset[1] or 0,offset.y or offset[2] or 0}
-- 弧度
polyon.rotation = math.rad(changeRotationToInverse(rotation))
--- 模板顶点,相对于offset为原点的顶点数组
polyon._tempVertices = {}
for i, vertice in ipairs(vertices) do
local x = vertices[i][1]
local y = vertices[i][2]
table.insert(polyon._tempVertices,{x=x,y=y})
end
--顶点数组,实际顶点坐标