四叉树碰撞检测算法优化

AS3 专栏收录该内容
10 篇文章 1 订阅

先看一下效果 同屏100个对象
这里写图片描述

四叉树算法的优点是检测效率和对象数量无关,只和树的深度有关

该算法广泛运用在游戏AI搜索,多物体碰撞检测等场合。

建树

    var levelIndex:int = 0;
            var columeIndex:int;
            var rowIndex:int;
            while(levelIndex < maxTreeLevel){//遍历每一层
                nColumeCount = 1 << levelIndex;
                nLeavesTotal = Math.pow(4,levelIndex);//每层叶子结点数量1,4,16,64  4的N次幂

                //构建一个数组,存储每一层的所有子节点  因为第一层是没有父节点的 所以用levelIndex+1
                aryLeavesArry = treeNodeVec2[levelIndex+1] = new Vector.<QuadNode>(nLeavesTotal,true);

                //节点size
                nNodeWidth = nMapWidth >> levelIndex;
                nNodeHeight = nMapHeight >> levelIndex;

                columeIndex = 0;
                //构建行,列 map
                while(columeIndex < nColumeCount){
                    rowIndex = 0;
                    //行列值肯定是相等的 比如 1*1 4*4 16*16 64*64
                    while(rowIndex < nColumeCount){
                        vQuadNode = new QuadNode();
                        vQuadNode.left = rowIndex * nNodeWidth;
                        vQuadNode.right = rowIndex * nNodeWidth + nNodeWidth;
                        vQuadNode.top = columeIndex * nNodeHeight;
                        vQuadNode.bottom = columeIndex * nNodeHeight + nNodeHeight;

                        //最后一层不用创建子节点 没有孩子了
                        if(levelIndex < maxTreeLevel - 1){
                            vQuadNode.children = new Vector.<QuadNode>();
                        }

                        //有孩子的就存起来,这个index可以这样理解,第一层就一个 index = 0
                        //第二层有4个  0,1,2,3
                        //第三层有16个 4,5,6,7,,,,15 推到一下就知道怎么算了 
                        var index:int = columeIndex * nColumeCount + rowIndex; //方格子嘛!
                        vQuadNode.level = levelIndex+1;
                        vQuadNode.index = index;
                        aryLeavesArry[index] =  vQuadNode;

                        //关联父子节点 treeNodeVec2[0]是空的,因为第一层是没有父节点的
                        if(treeNodeVec2[levelIndex] != null){
                            var parentIndex:int = index >> 2;//除以四就能算出父节点的index来了,自己画图验证吧
                            vQuadNode.Parent = treeNodeVec2[levelIndex][parentIndex];
                            vQuadNode.Parent.children.push(vQuadNode);//太他妈巧妙了
                        }
                        rowIndex++;
                    }
                    columeIndex++;
                }
                levelIndex++;
            }//建树完毕

            trace('建树完毕');

            //构建个对象池
            proxyPool = new Vector.<QObjBounds>(1<<9,true);

            for (var i:int = 0; i < proxyPool.length; i++) 
            {
                var obj:QObjBounds = new QObjBounds();
                obj.id = i;
                obj.nextId =i +1;
                proxyPool[i] = obj;
            }

优化算法部分,查找结点,采用等比缩放,异或位运算实现快速节点查找

    /**
         * 查找结点 
         * @param p
         * @return 
         * 
         */     
        private function findNode(p:QObjBounds):QuadNode
        {
            // TODO Auto Generated method stub
            //坐标映射到  节点上
            var n_left:int = p.left * m_nScaleW;
            var n_top:int = p.top * m_nScaleH;
            var n_right:int = p.right * m_nScaleW;
            var n_bottom:int = p.bottom * m_nScaleH;

            //异或操作  求出对现所在 节点层级  如果不理解,可以把 层级映射成二进制来表示
            var m_nXRange:int = n_left ^ n_right;
            var m_nYRange:int = n_top ^ n_bottom;

            var nodeLevel:int = maxTreeLevel;
            while((m_nXRange + m_nYRange) !=0){
                m_nXRange = m_nXRange >> 1;
                m_nYRange = m_nYRange >> 1;
                nodeLevel -- ;
            }
            if(nodeLevel == 0){
                trace("daddsd");
            }

            if(nodeLevel == 0)nodeLevel =1;
            //这里为啥要有移位,很多同学不理解,举个例子 
            //例如 一个深度是3的四叉树 上面的 top left 都是基于最高层级去算的 
            //也就是第三层,那么你算出来发现显示对象在第二层,
            //就需要我们把当前的top,left缩放到第二层的大小,
            //再根据 列数 * 维度 + 行数 求出索引,缩放多少层呢  就是下面的差
            var m_nShiftCount:int = maxTreeLevel - nodeLevel;
            n_left = n_left >> m_nShiftCount;
            n_top = n_top >> m_nShiftCount;

            //找到层级后,接着找具体的节点索引 
            //索引  = 列数 * 维度 + 行数    比如 3层  16个节点的树  当代理对象在第15个空间中
            // 行数 = 4 列数 = 3 维度 4*4 
            //          if(nodeLevel == 0)nodeLevel =1;
            var weishu:int = 1 << (nodeLevel - 1);
            var index:int =  n_top * weishu + n_left;
            var quadNode:QuadNode = treeNodeVec2[nodeLevel][index];
            return quadNode;
        }


碰撞检测
/**
     * 检测碰撞 
     * 
     */     
    private function checkCollision(checkObj:QObjBounds):void
    {
        // TODO Auto Generated method stub
        var vNode:QuadNode;
        var nextObj:QObjBounds;
        var preObj:QObjBounds;

        vNode = checkObj.node;
        var nodeLevel:int = vNode.level;

        //检测当前结点对象
        nextObj = checkObj.nextInNode;
        while(nextObj){
            if(checkObj.intersect(nextObj)){
                //                  nextObj.color = 0xFFFF00FF;
                //                  checkObj.color = 0xFFFF00FF;
                nextObj.isCollision = checkObj.isCollision = true;
            }
            nextObj = nextObj.nextInNode;
        }

        //检查 跨越所有子节点
        var level:int = maxTreeLevel;
        var nLineStart:int= checkObj.left*m_nScaleW;
        var nLineEnd:int = checkObj.right*m_nScaleW;
        var nColumeStart:int = checkObj.top*m_nScaleH;
        var nColumeEnd:int = checkObj.bottom*m_nScaleH;

        while(level!=0){
            for (var i:int = nColumeStart; i <= nColumeEnd; i++) 
            {
                for (var j:int = nLineStart; j <= nLineEnd; j++) 
                {
                    var index:int =int( i * (1 << (level -1)))+j;
                    vNode = treeNodeVec2[level][index];

                    //检测当前结点对象
                    nextObj = vNode.proxyListHead;
                    while(nextObj){
                        if(nextObj != checkObj){//当前检测的不是原来的
                            if(checkObj.intersect(nextObj)){
                                //                                  nextObj.color = 0xFFFF00FF;
                                //                                  checkObj.color = 0xFFFF00FF;
                                nextObj.isCollision = checkObj.isCollision = true;
                            }
                        }
                        nextObj = nextObj.nextInNode;
                    }
                }
            }
            //缩小包围圈
            nLineStart>>=1;
            nLineEnd>>=1;
            nColumeStart>>=1;
            nColumeEnd>>=1;
            level --;
            if(nodeLevel == level)break;
        }

    }

“`

源码及问题请留言或联系作者

  • 1
    点赞
  • 0
    评论
  • 5
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值