四叉树碰撞检测算法优化

先看一下效果 同屏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
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
叉树碰撞算法是一种常用的空间分割算法,可以有效地优化碰撞检测的性能。下面是使用Lua语言实现四叉树碰撞算法的基本步骤: 1. 定义四叉树节点 首先我们需要定义四叉树节点的数据结构,一个四叉树节点包含四个子节点和一个节点范围(即矩形区域)。可以使用Lua的table来表示节点。 ```lua local QuadNode = { children = {}, -- 子节点 bounds = {}, -- 节点范围 objects = {}, -- 包含的物体 } ``` 2. 构建四叉树 接下来,我们需要构建四叉树。构建四叉树的过程就是不断地将物体插入到四叉树节点中,如果一个节点已经包含了足够数量的物体或者达到了最大深度,则需要将该节点分割成四个子节点。 ```lua function QuadNode:insert(obj) if #self.objects < MAX_OBJECTS or self.depth >= MAX_DEPTH then table.insert(self.objects, obj) else if not self.children[1] then self:split() end for i = 1, #self.children do if self.children[i]:contains(obj) then self.children[i]:insert(obj) break end end end end function QuadNode:split() local x, y, w, h = self.bounds.x, self.bounds.y, self.bounds.width, self.bounds.height local half_w, half_h = w / 2, h / 2 self.children[1] = QuadNode:new(x, y, half_w, half_h, self.depth + 1) self.children[2] = QuadNode:new(x + half_w, y, half_w, half_h, self.depth + 1) self.children[3] = QuadNode:new(x, y + half_h, half_w, half_h, self.depth + 1) self.children[4] = QuadNode:new(x + half_w, y + half_h, half_w, half_h, self.depth + 1) for i = 1, #self.objects do local obj = self.objects[i] for j = 1, #self.children do if self.children[j]:contains(obj) then self.children[j]:insert(obj) break end end end self.objects = {} end ``` 3. 碰撞检测 最后,我们需要进行碰撞检测。对于一个物体,我们首先找到它所在的叶子节点,然后检测该节点中所有物体是否与该物体相交。如果相交,则进行进一步的检测。 ```lua function QuadNode:query(obj, results) if not results then results = {} end for i = 1, #self.objects do local other = self.objects[i] if obj ~= other and obj:intersects(other) then table.insert(results, other) end end if self.children[1] then for i = 1, #self.children do if self.children[i]:intersects(obj) then self.children[i]:query(obj, results) end end end return results end ``` 完整代码如下: ```lua QuadNode = { MAX_OBJECTS = 10, MAX_DEPTH = 5, } function QuadNode:new(x, y, w, h, depth) local node = { children = {}, bounds = {x = x, y = y, width = w, height = h}, objects = {}, depth = depth or 0, } setmetatable(node, {__index = QuadNode}) return node end function QuadNode:insert(obj) if #self.objects < QuadNode.MAX_OBJECTS or self.depth >= QuadNode.MAX_DEPTH then table.insert(self.objects, obj) else if not self.children[1] then self:split() end for i = 1, #self.children do if self.children[i]:contains(obj) then self.children[i]:insert(obj) break end end end end function QuadNode:split() local x, y, w, h = self.bounds.x, self.bounds.y, self.bounds.width, self.bounds.height local half_w, half_h = w / 2, h / 2 self.children[1] = QuadNode:new(x, y, half_w, half_h, self.depth + 1) self.children[2] = QuadNode:new(x + half_w, y, half_w, half_h, self.depth + 1) self.children[3] = QuadNode:new(x, y + half_h, half_w, half_h, self.depth + 1) self.children[4] = QuadNode:new(x + half_w, y + half_h, half_w, half_h, self.depth + 1) for i = 1, #self.objects do local obj = self.objects[i] for j = 1, #self.children do if self.children[j]:contains(obj) then self.children[j]:insert(obj) break end end end self.objects = {} end function QuadNode:contains(obj) local x, y, w, h = self.bounds.x, self.bounds.y, self.bounds.width, self.bounds.height local ox, oy, ow, oh = obj.x, obj.y, obj.width, obj.height return ox >= x and ox + ow <= x + w and oy >= y and oy + oh <= y + h end function QuadNode:intersects(obj) local x, y, w, h = self.bounds.x, self.bounds.y, self.bounds.width, self.bounds.height local ox, oy, ow, oh = obj.x, obj.y, obj.width, obj.height return x < ox + ow and x + w > ox and y < oy + oh and y + h > oy end function QuadNode:query(obj, results) if not results then results = {} end for i = 1, #self.objects do local other = self.objects[i] if obj ~= other and obj:intersects(other) then table.insert(results, other) end end if self.children[1] then for i = 1, #self.children do if self.children[i]:intersects(obj) then self.children[i]:query(obj, results) end end end return results end ``` 使用示例: ```lua -- 创建四叉树 local tree = QuadNode:new(0, 0, 640, 480) -- 插入物体 tree:insert({x=10, y=10, width=20, height=20}) tree:insert({x=30, y=30, width=20, height=20}) tree:insert({x=50, y=50, width=20, height=20}) -- 查询物体相交的物体 local results = tree:query({x=40, y=40, width=20, height=20}) for i = 1, #results do print('object', i, results[i].x, results[i].y) end ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值