碰撞检测系列——多边形与圆碰撞/相交

此文章是碰撞检测系列的第十二篇,多边形和圆形碰撞检测/相交,此系列主要包含了多种形状的碰撞/相交检测方法。

预览

先查看效果吧,点击这里

碰撞/相交检测方法

关于多边形与圆碰撞/相交的检测,我们可以简化问题,可以将检查多边形的每条边与圆是否碰撞/相交。线与圆的碰撞/相交检查,在之前章节有介绍过。利用线与圆碰撞/相交方法检查多边形每条边是否与圆相交。

kotlin 复制代码
let collision = this.lineCircle([vc, vn],c);
if (collision) return true;

完整代码如下:

javascript 复制代码
/**
 * 
 * @param {Array} vertices poly对象/多边形对象 结构[{x,y},{x,y},...]
 * @param {Object} c 圆对象{x,y,r} x/y: 圆心坐标; r:圆半径
 * @returns boolean
 */
function polygonCirecle(vertices,c) {
    // go through each of the vertices, plus
    // the next vertex in the list
    let next = 0;
    for (let current=0; current<vertices.length; current++) {

        // get next vertex in list
        // if we've hit the end, wrap around to 0
        next = current+1;
        if (next == vertices.length) next = 0;

        // get the PVectors at our current position
        // this makes our if statement a little cleaner
        let vc = vertices[current];    // c for "current"
        let vn = vertices[next];       // n for "next"

        // check for collision between the circle and
        // a line formed between the two vertices
        let collision = this.lineCircle([vc, vn],c);
        if (collision) return true;
    }

    // the above algorithm only checks if the circle
    // is touching the edges of the polygon -- in most
    // cases this is enough, but you can un-comment the
    // following code to also test if the center of the
    // circle is inside the polygon

    let centerInside = this.polygonPoint(vertices,c);
    if (centerInside) return true;

    // otherwise, after all that, return false
    return false;
}

主要代码

在我的demo中,当点与矩形碰撞/相交改变固定圆的颜色,可以点上面预览进去试试。这里是部分核心代码,详细代码结构解析点击这里 这里主要是渲染和交互代码,设置baseShape绘制参数为polygon, drawOpt中是绘制多边形的顶点位置;还有必须配置的hitFunc函数

ini 复制代码
const init = readyInit({
	baseShape: "polygon",
	radius1: 40,
	drawOpt: [{x:600,y:100},{x:800,y:100},{x:1000,y:300},{x:400,y:300}],
	hitFunc: (e, drawOpt, {radius1}) => {
		return hit.polygonCirecle(drawOpt,{ x:e.x, y:e.y, r:radius1})
	}
})

// 图形渲染以及交互
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";
            }
        }
        // 相交的辅助点绘制,不是每个demo都会有
        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) => {
        // 调用每个demo配置的hitFunc,检测碰撞结果
        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();
}

代码下载

以上代码只是主要代码并不是完整代码,由于完整代码较多就不贴出来了,有需要可以点击这里,这是GitHub的代码库,详细代码结构解析点击这里

相关推荐
蛋蛋_dandan1 天前
Fabric.js从0到1实现图片框选功能
canvas
wayhome在哪3 天前
用 fabric.js 搞定电子签名拖拽合成图片
javascript·产品·canvas
德育处主任4 天前
p5.js 掌握圆锥体 cone
前端·数据可视化·canvas
德育处主任4 天前
p5.js 3D 形状 "预制工厂"——buildGeometry ()
前端·javascript·canvas
德育处主任6 天前
p5.js 3D盒子的基础用法
前端·数据可视化·canvas
掘金安东尼7 天前
2分钟创建一个“不依赖任何外部库”的粒子动画背景
前端·面试·canvas
百万蹄蹄向前冲7 天前
让AI写2D格斗游戏,坏了我成测试了
前端·canvas·trae
用户2519162427119 天前
Canvas之画图板
前端·javascript·canvas
FogLetter12 天前
玩转Canvas:从静态图像到动态动画的奇妙之旅
前端·canvas
用户25191624271113 天前
Canvas之贪吃蛇
前端·javascript·canvas