[开发者心得] 不使用触发器,判断玩家是否在一个区域内(多边形)

[复制链接]
1038 |8
KumaIsKarma 发表于 2023-4-28 17:57:59 | 显示全部楼层 |阅读模式
本帖最后由 季理GiLiGiLi 于 2023-11-7 19:26 编辑

比如,玩家在某个区域内不想进行某一操作,放触发器又要太多太多太麻烦了,可以通过射线检测法,不放触发器就能检测玩家是否在某一个区域内。无论是凸多边形还是凹多边形。
原理:
求解从该点向右发出的水平线射线与多边形各边的交点,当交点数为奇数,则在内部。
    不过要注意几种特殊情况:1、点在边或者顶点上;2、点在边的延长线上;3、点出发的水平射线与多边形相交在顶点上(很多时候不需要那么准确 所以可以不用考虑特殊情况)

image.png



算法思路:

计算多边形每条边的线性函数,然后从玩家位置发出一条射线 检测是否有交点。



  /**
     * 射线法检测某点是否在一个区域内 适用于任何多边形 数组Polygon为多边形的每个点(可按顺序连点成线 最终化为一个多边形)
     */
    public laserCheckInPolygon(actorPos: Type.Vector) {
        if (this._nowPolygon == null || undefined) {
            return false;
        }
        let crossings = 0;  //越过的射线数量
        for (let i = 0; i < this._nowPolygon.length - 1; i++) {
            //检测玩家位置是否在该条边的范围内
            let condition1 = actorPos.x > this._nowPolygon[i].PosX && actorPos.x < this._nowPolygon[i + 1].PosX;
            let condition2 = actorPos.x < this._nowPolygon[i].PosX && actorPos.x > this._nowPolygon[i + 1].PosX;
            //计算斜率
            let slope = (this._nowPolygon[i + 1].PosY - this._nowPolygon[i].PosY) / (this._nowPolygon[i + 1].PosX - this._nowPolygon[i].PosX);
            //检测发出的射线是否有交点
            let y = slope * (actorPos.x - this._nowPolygon[i].PosX) + this._nowPolygon[i].PosY;
            if ((condition1 || condition2) && y > actorPos.y) {
                crossings++;
                console.log(`=======通过边${i}-${i + 1}`);
            }
        }
        let idx = this._nowPolygon.length - 1;
        let condition1 = actorPos.x > this._nowPolygon[idx].PosX && actorPos.x < this._nowPolygon[0].PosX;
        let condition2 = actorPos.x < this._nowPolygon[idx].PosX && actorPos.x > this._nowPolygon[0].PosX;
        let slope = (this._nowPolygon[0].PosY - this._nowPolygon[idx].PosY) / (this._nowPolygon[0].PosX - this._nowPolygon[idx].PosX);
        let y = slope * (actorPos.x - this._nowPolygon[idx].PosX) + this._nowPolygon[idx].PosY;
        if ((condition1 || condition2) && y > actorPos.y) {
            crossings++;
            console.log(`=======通过边${idx}-${0}`);
        }
        console.log(`==================射线通过的条数:${crossings}`);
        return crossings % 2 != 0;
    }


这个polygon数组可以看成就是一个vector数组,可按照数组0·length的顺序将他们连接起来绘制成一个简单封闭图形(边与边不交叉),所以就可以写一个循环,每两个点算一元一次方程,求交点。
这里可以总结为用一个点集构造简单封闭图形。
思路:取x坐标最大的点A(如果最大x坐标的点不止一个,则取Y坐标最小的点),依次计算A点与其余各点的连线与水平线之间夹角的正切值,然后按照正切值排序,依次连接排序后的各点即组成一个简单图形。

原理:其它所有点都在A点的左侧,所有夹角的范围为-Pi/2~Pi/2,y=tanX是个单调递增函数。


/**
     * 通过计算正弦值 绘制多边形
     */
    drawPolygon() {
        let polygon = MapManager.Polygon.filter((cell) => {
            return cell.CellObj.getVisibility()
        });
        polygon = polygon.sort((a, b) => { //按照posX从大到小排序
            return b.PosX - a.PosX;
        });
        /**离得最远的点 */
        let farestPoint = polygon[0];
        farestPoint.setPrimary();
        let cellsMap: [number, number][] = [];
        for (let i = 1; i < polygon.length; i++) { //计算每个点与最远的点向量 的正弦值
            let Ax = farestPoint.PosX - polygon[i].PosX;
            let Ay = farestPoint.PosY - polygon[i].PosY;
            let tan;
            if (Ax != 0) {
                tan = Ay / Ax;
            } else {
                tan = Infinity;
            }
            cellsMap.push([i, tan]);    //点和正弦值的映射;
        }
        console.log(`=========cellsMap1:${cellsMap}`);
        cellsMap = cellsMap.sort((a, b) => {   //按照正弦值 从大到小排
            return b[1] - a[1];
        });
        console.log(`=========cellsMap2:${cellsMap}`);
        let newPoly: Cell[] = [];
        newPoly.push(farestPoint);
        cellsMap.forEach((e) => {
            console.log(polygon[e[0]].PosX, polygon[e[0]].PosY);
            newPoly.push(polygon[e[0]]);
        });
        this._nowPolygon = newPoly;
        //连点成线
        for (let i = 0; i < polygon.length - 1; i++) {
            newPoly[i].shootLaser(newPoly[i + 1].Pos);
        }
        newPoly[polygon.length - 1].shootLaser(newPoly[0].Pos);

    }


AreaCheckStudy.rar (100.82 KB, 下载次数: 58)
回复

使用道具 举报

空伊伊 发表于 2023-4-28 18:05:08 | 显示全部楼层
数组Polygon具体内容是什么呢,这个代码看起来是从一个class中直接复制出来的
回复

使用道具 举报

喵喵哭唧唧 发表于 2023-4-28 18:05:11 | 显示全部楼层
好哥哥,能再娇娇我吗,比如说this._nowPolygon的定义,或者再举个例子,他的参数怎么设
回复

使用道具 举报

KumaIsKarma楼主 发表于 2023-4-28 18:12:58 | 显示全部楼层
这个polygon数组可以看成就是一个vector数组,可按照数组0·length的顺序绘制成一个多边形,所以就可以写一个循环,每两个点算一元一次方程,求交点。稍等 我贴一下 polygon的代码
回复

使用道具 举报

KumaIsKarma楼主 发表于 2023-4-28 18:29:03 | 显示全部楼层
空伊伊 发表于 2023-4-28 18:05
数组Polygon具体内容是什么呢,这个代码看起来是从一个class中直接复制出来的 ...

这个polygon数组可以就看成一个vector数组,可按照数组0·length的顺序将他们连接起来绘制成一个简单封闭图形(边与边不交叉),所以就可以写一个循环,每两个点算一元一次方程,求交点。已更新如何将一个无序点集变成一个有序可绘制一个简单封闭图形的点集的计算方式和原工程。
回复

使用道具 举报

空伊伊 发表于 2023-4-28 18:30:07 | 显示全部楼层
KumaIsKarma 发表于 2023-4-28 18:29
这个polygon数组可以就看成一个vector数组,可按照数组0·length的顺序将他们连接起来绘制成一个简单封闭 ...

给力
回复

使用道具 举报

KumaIsKarma楼主 发表于 2023-4-28 18:45:52 | 显示全部楼层
喵喵哭唧唧 发表于 2023-4-28 18:05
好哥哥,能再娇娇我吗,比如说this._nowPolygon的定义,或者再举个例子,他的参数怎么设 ...

喵喵,已更新~~~
回复

使用道具 举报

喵喵哭唧唧 发表于 2023-4-28 18:47:52 | 显示全部楼层

太强了,KK小姐姐,我在认真学习中
回复

使用道具 举报

kk 发表于 2023-4-28 21:42:46 | 显示全部楼层
泰裤辣
回复

使用道具 举报

热门版块
快速回复 返回顶部 返回列表