纯Graphics做的2d视野,躲猫猫demo

准备做个躲猫猫的demo,先实现视野有时间再做做别的

首先看预览,一个小人在城市里穿行

原理图

然后是代码
https://github.com/crytion111/visionAndMask

第一步很简单, 根据墙体的节点判断墙体的顶点位置, 理论支持各种凸多边体

// 传入矩形, 返回4个点
getRectFourPoint(node:cc.Node) : cc.Vec3[]
{
    let nWidth = node.width / 2;
    let nHeight = node.height / 2;

    //坐标转化成世界坐标系, 因为墙体的节点和玩家不一定在同一根节点下
    let p1 = node.convertToWorldSpaceAR(cc.v3(-nWidth, -nHeight));   //左下
    let p2 = node.convertToWorldSpaceAR(cc.v3(+nWidth, -nHeight));   //右下
    let p3 = node.convertToWorldSpaceAR(cc.v3(+nWidth, +nHeight));   //右上
    let p4 = node.convertToWorldSpaceAR(cc.v3(-nWidth, +nHeight));   //左上

    let posArr = [p1, p2, p3, p4]
    return posArr;
}

第二步,计算出这个墙体的所有顶点和主角的向量, 然后算出哪两个顶点离的最远,也就是向量夹角最大,那么这两个顶点肯定就是视野边缘

// 检测矩形和某个点的边界. 找出夹角最大的两个角
/**@
 * @param posArr        矩形的4个顶点
 * @param posPlayer     玩家位置
 * @param targetNode    玩家节点
 */
checkRectEdge(posArr:cc.Vec3[], posPlayer:cc.Vec3, targetNode:cc.Node): cc.Vec3[]
{
    // 存放向量用于计算
    let angleArr:cc.Vec3[] = [];
    for (let tempWorldPos of posArr)
    {
        //坐标转化, 因为getRectFourPoint中转出的是世界坐标,要变成主角所在的坐标系
        let tempPos = targetNode.parent.convertToNodeSpaceAR(tempWorldPos);

        let deX = tempPos.x - posPlayer.x
        let deY = tempPos.y - posPlayer.y
        angleArr.push(cc.v3(deX, deY));
    }


    let nMaxAngle = 0;
    let nMaxIndex = 0;
    let nMinIndex = 0;

    // 计算向量夹角,看看谁最大
    for (let i = 0; i < angleArr.length; i++)
    {
        let pos1 = angleArr[i];
        for (let j = i + 1; j < angleArr.length; j++)
        {
            let pos2 = angleArr[j];
            // 向量夹角计算公式,算出两个向量夹角, 那么夹角最大的肯定是视野边缘
            let cosO = ((pos2.x * pos1.x)+(pos2.y * pos1.y)) / (pos1.mag() * pos2.mag())
            let nJiaJiao = Math.acos(cosO) * (180 / Math.PI)
            //找出最大的夹角,然后记录下两个点的下标
            if (nJiaJiao > nMaxAngle)
            {
                nMaxAngle = nJiaJiao;
                nMaxIndex = i;
                nMinIndex = j;
            }
        }
    }
   //返回这个墙体的两个视野边缘点
    return [targetNode.parent.convertToNodeSpaceAR(posArr[nMaxIndex]),
        targetNode.parent.convertToNodeSpaceAR(posArr[nMinIndex])];
}

第三步, 画出计算的点围成的区域, 一共4个点围成这4个区域, 就是两个墙体边缘点,和两个延长线的终点

showGraLines()
{
    this.graphicsLine.clear();
    let posPlayer = this.nodePlayer.position;

    //遍历所有的墙体节点
    this.nodeWallRootArr.forEach((nodeRoot, index) =>
    {
        let nodeWall = nodeRoot.getChildByName("nodeWall")
        let posArr = this.getRectFourPoint(nodeWall);

        //拿到墙的节点,
        // let posArr = this.getRectFourPoint(nodeRoot);
        let drawPosArr = this.checkRectEdge(posArr, posPlayer, this.nodePlayer);

        /*  先画墙体边缘的点,
            再画两个延长的阴影区域,
            再往回画另一个墙体边缘的点
        */
        this.graphicsLine.moveTo(drawPosArr[0].x, drawPosArr[0].y); //先画墙体边缘的点,
        for (let i in drawPosArr)
        {
            let pos = drawPosArr[i];
            //将阴影部分至少延长到屏幕外面,就是放大墙体边缘和主角的向量长度
            let pos1111 = cc.v3((pos.x - posPlayer.x) * 9999, (pos.y - posPlayer.y) * 9999);
            //再画两个延长的阴影区域,
            this.graphicsLine.lineTo(posPlayer.x + pos1111.x, posPlayer.y + pos1111.y);
        }
        //再往回画另一个墙体边缘的点
        this.graphicsLine.lineTo(drawPosArr[1].x, drawPosArr[1].y);
    })
    //填充所有这4个点围成的区域
    this.graphicsLine.fill();
    this.graphicsLine.stroke();
}

游戏初始化了500个墙体,
image

但是遍历时只校验显示范围内的,所以也不怎么消耗性能

9赞

:grinning: :grinning: :grinning:

:ox::frog::ox::frog::ox::frog:

把项目丢上来。。

源码上传了,整个项目也没什么功能没什么图

牛皮 plus

下一步, 接上腾讯的联机引擎,做个躲猫猫demo

大神,如果是多边形咋办呢……

复刻一个 ape out 出来

大佬 666

不规则多边形就需要射线检测了吧,规则多边形和圆形都能通过简单的数学计算算出边角点

大佬NB啊