此文章是碰撞检测系列的第一篇,两点碰撞检测/相交,此系列主要包含了多种形状的碰撞/相交检测方法
预览
先查看效果吧,点击这里
碰撞/相交检测方法
两点之间的碰撞是最容易测试的。测试它们是否接触,只需检查它们的X和Y坐标是否相同!
if (x1 == x2 && y1 == y2) {
// points are in the same place: collision!
}
else {
// not colliding
}
我们可以将这些代码封装在一个函数中,以使其更可用。两个点的X/Y坐标作为参数传递。该函数返回一个布尔值true或false,取决于是否发生碰撞。
/**
*
* @param {Object} p1 点对象{x,y}
* @param {Object} p2 点对象{x,y}
* @returns boolean
*/
function pointPoint(p1, p2) {
// are the two points in the same location?
return p1.x == p2.x && p1.y == p2.y;
}
主要代码
在我的demo中,当两点碰撞/相交改变背景色,可以点上面预览进去试试。关于demo中的代码有需要注意的地方,由于点是很小的,碰撞很难触发。为了解决这个问题,我在demo中对canvas设置了缩放。详细代码解析点击这里
const init = readyInit({
zoom: 10,radius:1,radius1:1,
fillRectColliding: true,
hitFunc: (e, cp,{zoom}) => {
return hit.pointPoint(cp, utils.toPoint(Math.round(e.x/zoom), Math.round(e.y/zoom)))
}
})
function readyInit(opt) {
return () => {
initResize();
check(opt)
}
}
function initResize() {
resize()
window.addEventListener('resize', () => {
resize()
})
}
function check(opt) {
const ctx = utils.getCtx();
const canvas = ctx.canvas;
const zoom = opt.zoom || 1;
const width = canvas.width / zoom;
const height = canvas.height / zoom;
const cp = { x: Math.round(width / 2), y: Math.round(height / 2) }
ctx.scale(zoom, zoom)
const radius = opt.radius || 10;
const baseShape = opt.baseShape || 'circle'
let drawOpt = opt.drawOpt;;
if (baseShape === 'circle') {
drawOpt = {...cp, r:radius}
} else if (baseShape === 'rect') {
const w = opt.w || 400;
const h = opt.h || 200;
drawOpt = {x:(width - w) / 2, y:(height - h) / 2, w, h}
}
function render(colliding) {
utils.cleanCanvas(ctx)
ctx.fillStyle = '#0095d9E0';
ctx.strokeStyle = '#0095d9E0';
if (colliding) {
if (opt.fillRectColliding) {
ctx.save()
ctx.fillStyle = "#f6ad49";
ctx.fillRect(0, 0, width, height);
ctx.restore()
} else {
ctx.fillStyle = "#f6ad49E0";
ctx.strokeStyle = "#f6ad49E0";
}
}
const hitPoints = hit.hitPoints;
if (hitPoints) {
ctx.save()
ctx.fillStyle = "red";
hitPoints.forEach(p => {
drawUtils.circle(ctx, { x: p.x, y: p.y, r: 16 })
});
ctx.restore()
}
ctx.lineWidth = 20;
ctx.lineJoin = "round";
ctx.lineCap = "round";
const drawFunc = drawUtils[baseShape];
if (drawFunc) {
drawFunc(ctx,drawOpt)
}
delete hit.hitPoints;
}
const radius1 = opt.radius1 || 10;
const cursorShape = opt.cursorShape || 'circle'
canvas.addEventListener('mousemove', (e) => {
const colliding = opt.hitFunc ? opt.hitFunc(e, drawOpt, opt) : false;
render(colliding);
ctx.fillStyle = '#6a6868E0';
if (cursorShape === 'rect') {
const w = opt.cursorW || 20;
const h = opt.cursorH || 20;
drawUtils.rect(ctx, { x:e.x / zoom - w/2, y:e.y / zoom - h/2, w, h })
} else if (cursorShape === 'line') {
ctx.strokeStyle = "#6a6868E0";
ctx.lineWidth = 20;
ctx.lineJoin = "round";
ctx.lineCap = "round";
drawUtils.line(ctx, [opt.cursorStartPoint,{ x:e.x, y:e.y}])
} else if (cursorShape === 'polygon') {
const { x, y } = e;
const points = [
{ x: x - 20, y: y - 20 },
{ x: x + 40, y: y - 10 },
{ x: x + 60, y: y + 20 },
{ x: x - 20, y: y + 20 },
{x: x - 40, y: y},
]
drawUtils.polygon(ctx, points)
} else {
drawUtils.circle(ctx, { x:e.x / zoom, y:e.y / zoom, r:radius1 })
}
})
render();
}
注意:关于点的碰撞检测用的是完全相等的,在实际情况,这样做交互上可能不太丝滑,好点的方案是可以考虑一个容差值,两点的X/Y相减的绝对值小于等于容差值,就认为碰撞/相交
代码下载
以上代码只是主要代码并不是完整代码,由于完整代码较多就不贴出来了,有需要可以点击这里,这是GitHub的代码库,详细代码结构解析点击这里