搜索优化之四叉树算法(四)完结

很高兴在这里看到你,能够来到这里说明你已经对我的前三篇文章有所了解,也很欣慰你能有耐心阅读这些枯燥的文字和代码,由于时间关系没有写个可视化的小程序来形象说明这一系列的过程,不过也没有关系,我相信你的理解能力,相信你一定能从中有所收获;本章节是这个系列最后一篇,此系列教程一共四篇博文,由浅入深的阐述了四叉树的创建和使用过程,可以说是很详尽的描述了,包括每个细节部分,一己之见仅供参考,如果你有更好的思路,欢迎留言交流,不胜感激,好了,废话不多说了,接下来,我们开始今天的教程。

标题:四叉树搜索

通过前三节的努力,我们实现了四叉树的创建,并且可以很轻松的把显示对象添加到四叉树的节点上,当然,你也可以把它移除,放到其他的节点上,有了这些功能,完成显示对象在四叉树节点上的动态更新就是水到渠成的事情了,到目前为止,你会发现,自己对四叉树已经完全掌控了,你可以根据显示对象的坐标获取他所隶属节点的节点key,然后就可以从四叉树中获取到这个节点对象,假如我把显示对象的矩形范围转换成我的搜索范围,道理就相同的了,什么意思呢?,我们来从一个现有的游戏中举例说明一下,拿传奇霸业这款游戏来说吧,假象有一天早上,你吃过早饭,兴高采烈的去猪洞里面烧猪,刷一些血符精华,你会发现,好多猪啊,黑的红的,当你的鼠标光标从猪身上掠过的时候,即使有很多猪,你会惊奇的发现每个被你鼠标光标掠过的猪都会有一个被标记的变化,你点击它,它会被选中,对了,没错,这个就是我们今天要讲解的四叉树搜索,其实在你看不见的地形图上,野猪对象早已经被添加到某个四叉树节点中去了,每当你的光标移动一下,就会在帧事件中触发一次四叉树搜索,然而,这种搜索不会只搜索光标所指的一个像素点,那样也没有什么意义,这种所搜是基于一定范围的,比如我光标的位置是(1000,1000),那么我所搜的范围可能是(900,900,1100,1100)这样的矩形范围,即光标周围一定半径的矩形范围,这个半径的大小我们通常会根据显示对象的大小作为计算依据,比如一个怪物的高度是100像素,那么我们的半径就可以定为1小于100像素,比如50,在这个范围内搜索出所有的野猪,然后在遍历一下当前的点在哪个野猪身上,进而去高亮这个野猪实现选中的一种效果,好了,上面分析的已经很详尽了,下面我们来实现具体的搜索逻辑

先实现一个矩形相交判断的方法,两个矩形相交的判断依据是什么呢?
当两个矩形x的最大值 小于 两个矩形的right的最小值 可以断定水平方向上是相交的
当两个矩形y的最大值 小于 两个矩形的bottom的最小值,可以断定垂直方向是相交的

同时满足两个条件 判定为矩形相交

public static function intersect(curRect:Rectangle,tarRect:Rectangle):Boolean{
        return Math.max(curRect.x,tarRect.x) <= Math.min(curRect.right ,tarRect.right)&& 
            Math.max(curRect.y,tarRect.y) <= Math.min(curRect.bottom,tarRect.bottom);
    }

下面是具体的查找逻辑

/**
         * 核心方法-四叉树搜索查找 
         * @param rect
         * @param size
         * @return  查找区域内的所有显示对象列表
         * 
         */     
            public function find(rect:Rectangle,size:int):Array
            {
                if(!initialized)return null;

            var targetDepth:int =getDepth(this._mapSize,size) ;//显示对象所在深度

            var disList:Array = [];
            circleFind(disList,rect,_topNode,targetDepth);
            return disList;
        }

下面是从顶层节点循环遍历,当节点的深度和目标深度匹配时,取出节点内所有显示对象,进行矩形相交判断,如果相交就是我们想要的了

注意:这个方法有个地方需要优化,理想状态下我们搜索的矩形范围全部包含在节点范围里,假如搜索范围横跨几个节点区域,这样做只能计算出一个节点区域的,如果解决这个问题呢?

如下如所示,蓝色为理想情况,黄色框框为特殊情况

这里写图片描述

这种情况本章暂不考虑,待后续有空我在补上优化算法

/**
* 循环查找,从顶层节点开始逐层渗透
* @param disList 返回列表
* @param findRect 查找矩形范围
* @param curNode 当前节点
* @param targetDepth 目标深度
*
*/
private function circleFind(disList:Array, findRect:Rectangle,curNode:NodeRect, targetDepth:int):void
{
if(curNode==null)return;
if(IntersectUtils.intersect(curNode.rect,findRect) && curNode.length >0){//节点矩阵和搜索矩形相交

            if(curNode.depth == targetDepth){//深度为目标深度
                var allDis:Dictionary = curNode.disDic;
                for each (var item:NodeSprite in disList) 
                {
                    //显示对象在显示区域内
                    if(IntersectUtils.intersect(findRect,item.getRect(item.parent))){
                        disList.push(item);
                    }
                }

            }else{
                if(curNode.nodeA){
                    circleFind(disList,findRect,curNode.nodeA,targetDepth);
                }
                if(curNode.nodeB){
                    circleFind(disList,findRect,curNode.nodeB,targetDepth);
                }
                if(curNode.nodeC){
                    circleFind(disList,findRect,curNode.nodeC,targetDepth);
                }
                if(curNode.nodeD){
                    circleFind(disList,findRect,curNode.nodeD,targetDepth);
                }
            }
        }
    }

到此为止,本系列教程已经全部完结了,留下了一个问题作为探讨课题,同学们可以集思广益,大开脑洞,欢迎留言交流,再会!

猜你喜欢

转载自blog.csdn.net/gzylongxingtianxia/article/details/80077431
今日推荐