NAV导航网格寻路(5) -- 生成网格的一些补充

这篇是转的文章,原文http://blianchen.blog.163.com/blog/static/13105629920103811451196/

如果你也实现了上一章提到的代码,不难发现对下图的两种情况会出现问题

左面的是两个区域有相交的情况,右面的是多边形本身有自交,在这两种情况下,前面给出的代码均会产生错误的结果。

对于两个多边形相交,可以在生成网格之前先合并多边形,合并后如图

合并算法在前面多边形剪裁处已给出一个,这里只贴上代码:

 /**
         * 合并两个多边形(Weiler-Athenton算法)
         * @param polygon
         * @return
         *             null--两个多边形不相交,合并前后两个多边形不变
         *             Polygon--一个新的多边形
         */
        public function union(polygon:Polygon):Vector.<Polygon> {
            //包围盒不相交
            if (rectangle().intersection(polygon.rectangle()) == false) {
                return null;
            }

            //所有顶点和交点
            var cv0:Vector.<Node> = new Vector.<Node>();//主多边形
            var cv1:Vector.<Node> = new Vector.<Node>();//合并多边形
            //初始化
            var node:Node;
            for (var i:int=0; i<this.vertexV.length; i++) {
                node = new Node(this.vertexV[i], false, true);
                if (i > 0) {
                    cv0[i-1].next = node;
                }
                cv0.push(node);
            }
            for (var j:int=0; j<polygon.vertexV.length; j++) {
                node = new Node(polygon.vertexV[j], false, false);
                if (j > 0) {
                    cv1[j-1].next = node;
                }
                cv1.push(node);
            }

            //插入交点
            var insCnt:int = this.intersectPoint(cv0, cv1);

            //生成多边形
            if (insCnt > 0) {
                //顺时针序
                return linkToPolygon(cv0, cv1);
            } else {
                return null;
            }

            return null;
        }

        /**
         * 生成多边形,顺时针序; 生成的内部孔洞多边形为逆时针序
         * @param cv0
         * @param cv1
         * @return 合并后的结果多边形数组(可能有多个多边形)
         */
        private function linkToPolygon(cv0:Vector.<Node>, cv1:Vector.<Node>):Vector.<Polygon> {
            trace("linkToPolygon***linkToPolygon");
            //保存合并后的多边形数组
            var rtV:Vector.<Polygon> = new Vector.<Polygon>();

            //1. 选取任一没有被跟踪过的交点为始点,将其输出到结果多边形顶点表中.
            for each (var testNode:Node in cv0) {
                if (testNode.i == true && testNode.p == false) {
                    var rcNodes:Vector.<Vector2f> = new Vector.<Vector2f>();
                    while (testNode != null) {

                        testNode.p = true;

                        // 如果是交点
                        if (testNode.i == true) {
                            testNode.other.p = true;

                            if (testNode.o == false) {        //该交点为进点(跟踪裁剪多边形边界)
                                if (testNode.isMain == true) {        //当前点在主多边形中
                                    testNode = testNode.other;        //切换到裁剪多边形中
                                }
                            } else {                    //该交点为出点(跟踪主多边形边界)
                                if (testNode.isMain == false) {        //当前点在裁剪多边形中
                                    testNode = testNode.other;        //切换到主多边形中
                                }
                            }
                        }

                        rcNodes.push(testNode.v);          ////// 如果是多边形顶点,将其输出到结果多边形顶点表中

                        if (testNode.next == null) {    //末尾点返回到开始点
                            if (testNode.isMain) {
                                testNode = cv0[0];
                            } else {
                                testNode = cv1[0];
                            }
                        } else {
                            testNode = testNode.next;
                        }

                        //与首点相同,生成一个多边形
                        if (testNode.v.equals(rcNodes[0])) break;
                    }
                    //提取
                    rtV.push(new Polygon(rcNodes.length, rcNodes));
                }
            }

            trace("rtV", rtV);
            return rtV;
        }

        /**
         * 生成交点,并按顺时针序插入到顶点表中
         * @param cv0 (in/out)主多边形顶点表,并返回插入交点后的顶点表
         * @param cv1 (in/out)合并多边形顶点表,并返回插入交点后的顶点表
         * @return 交点数
         */
        private function intersectPoint(cv0:Vector.<Node>, cv1:Vector.<Node>):int {
            var insCnt:int = 0;        //交点数

            var findEnd:Boolean = false;
            var startNode0:Node = cv0[0];
            var startNode1:Node;
            var line0:Line2D;
            var line1:Line2D;
            var ins:Vector2f;
            var hasIns:Boolean;
            var result:int;        //进出点判断结果
            while (startNode0 != null) {        //主多边形
                if (startNode0.next == null) {  //最后一个点,跟首点相连
                    line0 = new Line2D(startNode0.v, cv0[0].v);
                } else {
                    line0 = new Line2D(startNode0.v, startNode0.next.v);
                }

                startNode1 = cv1[0];
                hasIns = false;

                while (startNode1 != null) {        //合并多边形
                    if (startNode1.next == null) {
                        line1 = new Line2D(startNode1.v, cv1[0].v);
                    } else {
                        line1 = new Line2D(startNode1.v, startNode1.next.v);
                    }
                    ins = new Vector2f();    //接受返回的交点
                    //有交点
                    if (line0.intersection(line1, ins) == LineClassification.SEGMENTS_INTERSECT) {
                        //忽略交点已在顶点列表中的
                        if (this.getNodeIndex(cv0, ins) == -1) {
                            insCnt++;

                            ///////// 插入交点
                            var node0:Node = new Node(ins, true, true);
                            var node1:Node = new Node(ins, true, false);
                            cv0.push(node0);
                            cv1.push(node1);
                            //双向引用
                            node0.other = node1;
                            node1.other = node0;
                            //插入
                            node0.next = startNode0.next;
                            startNode0.next = node0;
                            node1.next = startNode1.next;
                            startNode1.next = node1;
                            //出点
                            if (line0.classifyPoint(line1.getPointB()) == PointClassification.RIGHT_SIDE) {
                                node0.o = true;
                                node1.o = true;
                            }
                            //TODO 线段重合
//                            trace("交点****", node0);

                            hasIns = true;        //有交点

                            //有交点,返回重新处理
                            break;
                        }
                    }
                    startNode1 = startNode1.next;
                }
                //如果没有交点继续处理下一个边,否则重新处理该点与插入的交点所形成的线段
                if (hasIns == false) {
                    startNode0 = startNode0.next;
                }
            }
            return insCnt;
        }

        /**
         * 取得节点的索引(合并多边形用)
         * @param cv
         * @param node
         * @return
         */
        private function getNodeIndex(cv:Vector.<Node>, node:Vector2f):int {
            for (var i:int=0; i<cv.length; i++) {
                if (cv[i].v.equals(node)) {
                    return i;
                }
            }
            return -1;
        }

对于一个给定的多边形数组polygonV,可以象下面这样在三角化以前做预处理

        /**
         * 合并
         */
        private function unionAll():void {
            for (var n:int=1; n<polygonV.length; n++) {
                var p0:Polygon = polygonV[n];
                for (var m:int=1; m<polygonV.length; m++) {
                    var p1:Polygon = polygonV[m];
                    if (p0 != p1 && p0.isCW() && p1.isCW()) {
                        var v:Vector.<Polygon> = p0.union(p1);    //合并

                        if (v != null && v.length > 0) {
                            trace("delete");
                            polygonV.splice(polygonV.indexOf(p0), 1);
                            polygonV.splice(polygonV.indexOf(p1), 1);

                            for each (var pv:Polygon in v) {
                                polygonV.push(pv);
                            }

                            n = 1;    //重新开始
                            break;
                        }
                    }
                }
            }
        }

对于多边形自交,可以采用类似多边形剪裁的方法将一个多边形拆分成多个,也可以直接禁止用户绘制这种多边形。我是采用后一种方法所以这里没有代码。

多边形的顶点顺序,上面多处代码都要求多边形顶点按顺时针或逆时针方向保存,但是我们不可能要求用户按哪个固定方向绘制图形,那么怎么判断多边形的顶点顺序。方法的基本思路就是取一个多边形的凸点,然后判断这个点的下一个点的方向,代码如下:

/**
         * 将多边形的顶点按顺时针排序
         */
        public function cw():void {
            if (this.isCW() == false) {    //如果为逆时针顺序, 反转为顺时针
                this.vertexV.reverse();    //反转数组
            }
        }

        /**
         * clockwise
         * @return true -- clockwise; false -- counter-clockwise
         */
        public function isCW():Boolean {
            if (vertexV == null || vertexV.length < 0) return false;

            //最上(y最小)最左(x最小)点, 肯定是一个凸点
            //寻找最上点
            var topPt:Vector2f = this.vertexV[0];
            var topPtId:int = 0;    //点的索引
            for (var i:int=1; i<vertexV.length; i++) {
                if (topPt.y > vertexV[i].y) {
                    topPt = vertexV[i];
                    topPtId = i;
                } else if (topPt.y == vertexV[i].y) { //y相等时取x最小
                    if (topPt.x > vertexV[i].x) {
                        topPt = vertexV[i];
                        topPtId = i;
                    }
                }
            }

            //凸点的邻点
            var lastId:int = topPtId-1>=0 ? topPtId-1 : vertexV.length-1;
            var nextId:int = topPtId+1>=vertexV.length ? 0 : topPtId+1;
            var last:Vector2f = vertexV[lastId];
            var next:Vector2f = vertexV[nextId];

            //判断
            var r:Number = multiply(last, next, topPt);
            if (r < 0) {
                return true;
            //三点共线情况不存在,若三点共线则说明必有一点的y(斜线)或x(水平线)小于topPt
            }

            return false;
        }

        /**
         * r=multiply(sp,ep,op),得到(sp-op)*(ep-op)的叉积
         * r>0:ep在矢量opsp的逆时针方向;
         * r=0:opspep三点共线;
         * r<0:ep在矢量opsp的顺时针方向
         * @param sp
         * @param ep
         * @param op
         * @return
         */
        private function multiply(sp:Vector2f, ep:Vector2f, op:Vector2f):Number {
            return((sp.x-op.x)*(ep.y-op.y)-(ep.x-op.x)*(sp.y-op.y));
        }

到此生成网格的关键代码都发出来了,下一章放上寻路的代码

时间: 2024-10-07 19:28:37

NAV导航网格寻路(5) -- 生成网格的一些补充的相关文章

NAV导航网格寻路(4) -- 生成nav网格

这篇是转的文章,原文http://blianchen.blog.163.com/blog/static/131056299201037102315211/ 假设上图是一个游戏地图,红色的区域是不可行走的区域,浅灰色区域是可行走区域,要想在游戏中实现nav寻路,必须将可行走区域转化为nav网格并保存为一种固定形式的数据,如下图浅红色的三角形. nav网格必须是凸多边形,这里使用三角型,当然也可以使用4边形.下面介绍一种任意多边形的三角化算法.算法来自论文<平面多边形域的快速约束 三角化>作者:曾

NAV导航网格寻路 一些必要的计算几何知识

转载:http://blog.csdn.net/ynnmnm/article/details/44833007 NAV导航网格寻路 -- 一些必要的计算几何知识 在继续下面的nav网格生成算法之前,先介绍一下涉及到的计算几何知识.这里只罗列出结论,要详细了解参考相关书籍. 矢量加减法: 设二维矢量P = ( x1, y1 ),Q = ( x2 , y2 ),则矢量加法定义为: P + Q = ( x1 + x2 , y1 + y2 ),同样的,矢量减法定义为: P - Q = ( x1 - x2

Unity导航 (寻路系统Nav Mesh Agent)

第一种 简单寻路  地面接触到的.到达目标点不用跳跃能够一直走路到达. 场景视图中简单搭设几个物体.如图1 胶囊体为寻路者,黄球为目标点 红地板,绿色障碍物. 现将地板以及障碍物选中 在检视面板设置静态为Navigation Static 如图2 然后菜单栏选择窗口 Window –Navigation 然后选择All-Bake烘焙如图 有三个页面参数可以调节烘焙效果,参数具体参照圣典解释. 注意 胶囊体和目标物都要烘焙在蓝色格子里面.否则不能实现,可以调节第二个页面里的参数调节烘焙大小. 然后

numpy的生成网格矩阵 meshgrid()

numpy模块中的meshgrid函数用来生成网格矩阵,最简单的网格矩阵为二维矩阵 meshgrid函数可以接受 x1, x2,..., xn 等 n 个一维向量,生成 N-D 矩阵. 1 基本语法 meshgrid(*xi, **kwargs) 参数: xi - x1, x2,..., xn : array_like 返回值: X1, X2,..., XN : ndarray 2 示例 2.1 一个参数时 import numpy as np a = [1,2,3] b = np.meshgr

NAV导航栏———下拉菜单

利用CSS实现导航栏菜单—下拉菜单. 首先给出HTML下拉菜单布局格式: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Stylin' with CSS - Figure 6.5 Drop-Down Menus</title> <link rel="stylesheet" type="text/css&

零开始构建Angular项目----之路由配置 和 nav导航条

效果 接着上次 零开始构建Angular项目继续扯路由配置 和 nav导航条 1.增加about页面 about.component.html <!-- Docs nav ================================================== --> <div class="row"> <!-- <div class="col-md-3 "> <div class="bc-sid

【H5新增元素跟文档结构】新的文档结构 1. article 文章块 2. section 区块 3. nav 导航条

1. article 文章块 article 通常包括 header 跟 footer 结构 ① 用 article 设计一篇新闻稿 语句: 1 <article> 2 <header> 3 <h1>文章标题</h1> 4 <time pubdate="pubdate">2017年9月26日消息</time> 5 </header> 6 <p> 7 文章内容 8 </p> 9 &

NAV导航网格寻路(2) -- 寻路方法

这篇是转的文章,原文http://blianchen.blog.163.com/blog/static/1310562992010324046930/ nav寻路一般包含两部分,首先是使用工具根据地图信息生成寻路用的nav mesh,接下来就是在游戏中根据生成的nav mesh来自动寻路. 一般人首先关心的就是寻路方法,所以这里把顺序颠倒下,先说寻路. 一.  使用A*寻找所经过网格路径 下图为一个已经生成nav网格的地图,深红色区域为不可行走区域,浅红色区域为可以行走的区域. 如下图,现在如果

NAV导航网格寻路(7) -- 代码和一些优化

这篇是转的文章,原文http://blianchen.blog.163.com/blog/static/131056299201031293039882/ 这里发不了源码,本系列完整源码可以到http://bbs.9ria.com/thread-49841-1-1.html下. 看下图,最优路径应该是从上面绕过中间阻挡区,而实际寻路产生的路径确是下面.这是由于,在网格面积过大或有某边长度过长时,由于a*中的花费是计算网格的两边中点距离而不实际的路径长度,所以产生的路径偏差较大.所以在一般的网格生