HT for Web可視化QuadTree四叉樹碰撞檢測

QuadTree四叉樹顧名思義就是樹狀的數據結構,其每一個節點有四個孩子節點,可將二維平面遞歸分割子區域。QuadTree經常使用於空間數據庫索引,3D的椎體可見區域裁剪,甚至圖片分析處理,咱們今天介紹的是QuadTree最常被遊戲領域使用到的碰撞檢測。採用QuadTree算法將大大減小須要測試碰撞的次數,從而提升遊戲刷新性能,本文例子基於HT for Web的圖形引擎,經過GraphViewGraph3dView共享同一數據模型DataModel,同時呈現QuadTree算法下的2D和3D碰撞視圖效果:http://v.youku.com/v_show/id_XODQyNTA1NjY0.htmlphp

Screen Shot 2014-12-06 at 12.41.24 AM

QuadTree的實現有不少成熟的版本,我選擇的是 https://github.com/timohausmann/quadtree-js/ 四叉樹的算法很簡單,所以這個開源庫也就兩百來行代碼。使用也很是簡單,構建一個Quadtree對象,第一個參數傳入rect信息制定遊戲空間範圍,在每次requestAnimationFrame刷新幀時,先經過quadtree.clear()清除老數據,經過quadtree.insert(rect)插入新的節點矩形區域,這樣quadtree就初始化好了,剩下就是根據須要調用quadtree.retrieve(rect)獲取指定矩形區域下,與其可能相交須要檢測的矩形對象數組。html

我構建了HTGraphViewGraph3dView兩個組件,經過ht.widget.SplitView左右分割,因爲兩個視圖都共享同一DataModel,所以咱們剩下的關注點僅是對DataModel的數據操做,構建了200個ht.Node對象,每一個對象的attr屬性上保存了隨機的運動方向vx和vy,同時保存了將要反覆插入quadtree的矩形對象,這樣避免每幀更新時反覆建立對象,同時矩形對象也引用了ht.Node對象,用來當經過quadtree.retrieve(rect)獲取須要檢測的矩形對象時,咱們能指定其所關聯的ht.Node對象,由於咱們須要對最終檢測爲碰撞的圖元設置上紅顏色的效果,也就是ht.Node平時顯示默認的藍色,當互相碰撞時將改變爲紅色。node

須要注意從quadtree.retrieve(rect)獲取須要檢測的矩形對象數組中會包含自身圖元,同時這些僅僅是可能會碰撞的圖元,並不意味着已經碰撞了,因爲咱們例子是矩形,所以採用ht.Default.intersectsRect(r1, r2)最終判斷是否相交,若是你的例子是圓形則能夠採用計算兩個圓心距離是否小於兩個半徑來決定是否相交,所以最終判斷的標準根據遊戲類型會有差別。git

採用了QuadTree仍是極大了提升了運算性能,不然100個圖元就須要100*100次的監測,我這個例子場景下通常也就100*(10~30)的量:http://v.youku.com/v_show/id_XODQyNTA1NjY0.htmlgithub

Screen Shot 2014-12-06 at 12.42.35 AM

除了碰撞檢測外QuadTree算法還有不少有趣的應用領域,有興趣能夠玩玩這個 https://github.com/fogleman/Quads算法

Screen Shot 2014-12-06 at 12.52.17 AM

全部代碼以下供參考:數據庫

function init(){  
    d = 200;
    speed = 8;
    dataModel = new ht.DataModel();                                
    g3d = new ht.graph3d.Graph3dView(dataModel);                                                  
    g2d = new ht.graph.GraphView(dataModel);                   
    mainSplit = new ht.widget.SplitView(g3d, g2d);                   
    mainSplit.addToDOM();                                        
    g2d.translate(300, 220);      
    g2d.setZoom(0.8, true);
      
    for(var i=0; i<100; i++) {
        var node = new ht.Node();
        node.s3(randMinMax(5, 30), 10, randMinMax(5, 30));
        node.p3(randMinMax(-d/2, d/2), 0, randMinMax(-d/2, d/2));
        node.s({
            'batch': 'group',
            'shape': 'rect',
            'shape.border.width': 1,
            'shape.border.color': 'white',
            'wf.visible': true,
            'wf.color': 'white'
        });
        node.a({
            vx: randMinMax(-speed, speed),
            vy: randMinMax(-speed, speed),
            obj: {
                width: node.getWidth(),
                height: node.getHeight(),
                data: node
            }
        });                    
        dataModel.add(node);
    }                
    createShape([
        {x: -d, y: d},
        {x: d, y: d},
        {x: d, y: -d},
        {x: -d, y: -d},
        {x: -d, y: d}
    ]);                   
    quadtree = new Quadtree({ x: -d, y: -d, width: d, height: d });                                
    requestAnimationFrame(update);
}               
function update() {   
    quadtree.clear();                
    dataModel.each(function(data){
        if(!(data instanceof ht.Shape)){
            var position = data.getPosition();
            var vx = data.a('vx');
            var vy = data.a('vy');
            var w = data.getWidth()/2;
            var h = data.getHeight()/2;
            var x = position.x + vx;
            var y = position.y + vy;
            if(x - w < -d){
                data.a('vx', -vx);
                x = -d + w;
            }
            if(x + w > d){
                data.a('vx', -vx);
                x = d - w;
            }
            if(y - h < -d){
                data.a('vy', -vy);
                y = -d + h;
            }
            if(y + h > d){
                data.a('vy', -vy);
                y = d - h;
            }
            data.setPosition(x, y);                        
            var obj = data.a('obj');
            obj.x = x - w;
            obj.y = y - h;
            
            quadtree.insert(obj);
            setColor(data, undefined);
        }
    });                
    dataModel.each(function(data){
        if(!(data instanceof ht.Shape)){ 
            var obj = data.a('obj');
            var objs = quadtree.retrieve(obj);
            if(objs.length > 1){                            
                for(var i=0; i<objs.length; i++ ) {
                    var data2 = objs[i].data;
                    if(data === data2){
                        continue;
                    }
                    if(ht.Default.intersectsRect(obj, data2.a('obj'))){
                        setColor(data, 'red');
                        setColor(data2, 'red');
                    }                                
                }                             
            }
        }
    });
    requestAnimationFrame(update);                    
}                        
function randMinMax(min, max) {
    return min + (Math.random() * (max - min));
}                      
function createShape(points){
    shape = new ht.Shape();
    shape.setPoints(points);
    shape.setThickness(4);
    shape.setTall(10);                                
    shape.s({
        'all.color': 'red',
        'shape.background': null,
        'shape.border.width': 2,
        'shape.border.color': 'red'                    
    });                
    dataModel.add(shape); 
    return shape;
}
function setColor(data, color){
    data.s({
        'all.color': color,
        'shape.background': color
    });
}
相關文章
相關標籤/搜索