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

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

预览

先查看效果吧,点击这里

碰撞/相交检测方法

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

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的代码库,详细代码结构解析点击这里

相关推荐
David+Zhao16 小时前
vue项目纯前端把PDF转成图片并下载
前端·vue.js·pdf·canvas·pdf转图片·pdfjs·pdfjs-dist
歡進1 天前
开源 | Warpvas 实现扭曲的画布
javascript·webgl·canvas
xiyueta2 天前
基于 uni-app 和 Vue3 开发的汉字书写练习应用
uni-app·canvas
sailven2 天前
【uniapp】图片添加canvas水印
uni-app·vue·canvas·拍照·图片水印
小和尚敲代码9 天前
uniapp中使用leaferui使用Canvas绘制复杂异形表格的实现方法
uni-app·canvas·leafer-game·leafer-ui
GDAL15 天前
HTML Canvas clip 深入全面讲解
前端·javascript·canvas
echoVic17 天前
PixiJS 源码揭秘 - 7. 事件系统源码解读
前端·源码阅读·canvas
伊丶二22 天前
微信小程序日程预约
前端·微信小程序·canvas
高心星22 天前
HarmonyOS 5.0应用开发——Canvas制作个人签名
华为·canvas·arkui·harmonyos5.0·个人签名
悠悠~飘22 天前
uniapp canvas 生成海报并保存到相册
前端·uni-app·canvas