簡體   English   中英

JavaScript沖突檢測的優化

[英]Optimization of JavaScript collision detection

首先-請不要刪除此帖子。 這不是重復的。

我知道它涵蓋了這里多次提到的問題,但是這次不是“如何檢測沖突”,因為您將在后面看到,它已經完成了。 盡可能多的優化方式更多地涉及“如何編寫”,因為低於檢測將在短時間內延遲多次觸發。

這是我的小提琴: http : //jsfiddle.net/slick/81y70h1f/

我生成隨機平方並檢測它們是否相互碰撞。

HTML使用以下方式生成。 沒有火箭科學:

<?php for ($i=1; $i<=$amount; $i++) { ?>
    <div id="square_<?= $i; ?>" class="square" style="top: <?= rand(0, 800); ?>px; left: <?= rand(0, 800); ?>px;">
        <div>square_<?= $i; ?></div>
    </div>
<?php } ?>

在小提琴中, $amount設置為16。您可以想象,唯一對組合的可能數量等於:

在此處輸入圖片說明

在小提琴中,您將看到我執行了兩次唯一性計算。 第二次只是為了不會碰撞的正方形。

var squares_without_collision = $(squares).not(garbage).get();
pairs_cleaned = get_unique_pairs(squares_without_collision);

當我將執行不屬於此問題的秘密操作時, pairs_cleaned是我的最終數組。 總是會因不必要的廢話而稍微減少該數組。

當我將$amount增加到100我會得到4950種可能的組合。 當我刷新頁面時,它仍然可以正常工作,但是我可以觀察到速度下降。 我什至沒有嘗試將其設置為200,因為我不希望瀏覽器崩潰。

問題-這里仍然有改進和優化的空間嗎? 因為現在我將揭示這些正方形將成為Google Map標記,並且在以下情況下會在事件觸發我的碰撞計算:

  1. 瓷磚已加載
  2. 地圖被拖動
  3. 縮放已更改

在最終版本中,我將顯示或隱藏標記,而不是將背景從綠色更改為紅色。 我擔心,使用更多標記會做烏龜腳本。 我想使其保持更快的速度。

好的,看一下,您有辦法解決它。 無需查找對,您經常查詢DOM方式。 每個元素只應觸摸一次DOM。 垃圾陣列使用信號量是多余的。 切勿在時間緊迫的代碼中使用each() ,因為它非常慢。

始終將變量保留在函數范圍內(在主函數內部),因為將變量保留在全局范圍內將使訪問速度提高一半。

陣列速度很慢,應不惜一切代價避免使用。 如果可以,請重用數組項。 總是問您是否真的需要一個新的陣列? 有沒有一種方法不使用數組?

不要在不需要的地方測試。 您有一些垃圾,但是您需要重新測試這些正方形。

避免在對時間要求嚴格的代碼循環內進行函數調用。 調用函數會占用大量CPU,因此最好內聯代碼。

避免索引到數組中。 引用數組項一次並使用引用。

除非有明確和合理的理由,否則避免使用JQuery。 jQuery的運行速度非常慢,並且會鼓勵過度使用DOM。

認為就是這樣。 以下是您對Fiddle進行的修改,它將運行得更快。

$(function () {
    var squares = [];  // keep arrays in function scope as out side the function
    var pairs_cleaned = []; // they are in global scope and run at half the speed.
    var x1,y1;
    squares = $('.square'); // get the squares
    var len = squares.length;
    console.log('----- Squares away ' + len + '------');
    console.log(squares);


    var width = 80+10;  // you can do this get the size and padding from the first square
    var height = 80+10; // if each square is a different size then you will have to change the code a little
    for(var i = 0; i < len; i += 1){ // itterate them. Avoid using Each in time critical code as it is slow       
        var div = squares[i];
        squares[i] = {  // replace the existing array with a new object containing all we will need. This reuses the array and avoids overheads when growing an array.
            square:div, // save the square. Not sure if you need it?
            garbage:false,     // flage as not garbage
            x: x1 = Number(div.offsetLeft),  // get the squares location
            y: y1 = Number(div.offsetTop),   // and ensure all values are Numbers
            b: y1 + height,  // I have only included the static height and width.
            r: x1 + width,  
        };                       

    }
    var s1,s2;
    for (var i = 0; i < len; i++) { // instead of calling the function to get an array of pairs, just pair them on the fly. this avoid a lot of overhead.
        s1 = squares[i]; // reference the item once outside the loop rather than many times inside the next loop
        for (var j = i + 1; j < len; j++) {
            if(!squares[j].garbage){ // ignore garbage
                s2 = squares[j];
                // do the test inside the loop rather than call a function. This avoids a lot of overhead
                if (s1.x > s2.r || s1.y > s2.b || s1.r < s2.x || s1.b < s2.y){ // do test
                    pairs_cleaned.push([s1,s2]); // if passed save unique pairs
                }else{
                    s2.square.style.backgroundColor = '#ff0040';  // this should not be here is Very very slowwwwwwwww
                    s2.garbage = true;  // garbage
                }
            }
        }
    }

    console.log('----- all pairs without garbage ------');
    console.log(pairs_cleaned);
});

好。 希望能有所幫助。 它已經運行並且可以在chrome上運行。 您將需要查看元素的位置和大小查詢,但是對於該示例,我認為它並不重要。

您還可以進行其他優化,但是如果您擺脫了s2.square.style.backgroundColor = '#ff0040';則可以實時看到1000平方左右的正方形s2.square.style.backgroundColor = '#ff0040'; 從內部循環。 它是整個碰撞測試循環中最慢的部分。 對於快速的代碼要求,DOM是致命的。 始終將所有DOM聯系人置於關鍵代碼部分之外。

最后一件事。 為了始終獲得最佳性能,請始終使用嚴格模式,這將使大多數代碼的性能提高20%以上。

您可以考慮為任務實現簡單的碰撞網格。 也就是說,采用跨越整個碰撞場的概念性2D網格,其中每個網格單元的大小都大於或等於碰撞節點的最大大小,然后將每個碰撞節點的中心點合並到表示碰撞點的數據結構中網格。

從那里開始,對於每個給定的碰撞節點,您只需要檢查與放置在當前碰撞節點的網格單元的任何相鄰網格單元中的其他節點的碰撞。

例如:

假設地圖的寬度和高度為1000像素,則碰撞節點以50x50像素的正方形表示。 您選擇實施一個100px x 100px的網格。

因此,您首先要創建一個由2D數組組成的數據結構,其中每個單元格都包含一個將存儲碰撞對象的數組:

var gridSize = { w: 1000, h: 1000 }; // The predefined grid size
var blockSize = { w: 100, h: 100 }; // The predefined block size

var collisionGrid = [];

// Initialize a grid of blockSize blocks to fill the gridSize
var x, y, gridX, gridY;
for (x = 0; x < gridSize.w; x += blockSize.w) {
    gridX = x/blockSize.w;
    collisionGrid[gridX] = [];
    for (y = 0; y < gridSize.h; y += blockSize.h) {
        gridY = x/blockSize.h;
        collisionGrid[gridX][gridY] = [];
    }
}

然后,當您了解碰撞節點的位置(例如,從某些API提取的數據)時,您將根據碰撞節點的中心點在網格上的位置填充對每個碰撞節點的引用的數據結構。

因此,具有{ x: 726, y:211, w: 50, h:50 }的正方形碰撞節點將像這樣放置:

var placeNode = function(node) {
    var mid = {
        x: node.x + node.w/2,
        y: node.y + node.h/2
    };
    var cell = {
        x: Math.floor(mid.x/blockSize.w),
        y: Math.floor(mid.y/blockSize.h)
    };
    collisionGrid[cell.x][cell.y].push(node);
};

var node = { x: 726, y:211, w: 50, h:50 } // ...fetched from some API
placeNode(node);

將數百或數千個節點放置在網格中后(每個節點只需很少的開銷-僅需一分為二或將一個引用推入數組),就可以大大減少檢查給定節點的沖突,因為您只需要檢查與當前節點的單元以及8個相鄰單元中的節點的沖突。

在此示例中,將針對給定節點檢查僅300x300px塊內的節點,但是隨着沖突字段大小的增加和網格大小/沖突節點大小的減小,此技術確實可以發揮作用。

在這篇博客文章中,我為我正在開發的游戲輕松解釋了這種碰撞網格的實現: http : //blog.cheesekeg.com/prototype-just-the-basics-v0-2/

要注意的一件事是,這里需要權衡-當碰撞節點四處移動時,當它們在網格上移動時,它們對應的引用從網格單元移動到網格單元。 但是,在上面我鏈接的文章中,當數百個碰撞節點在網格上移動時,這一事實不會對性能造成任何明顯的問題。

正如布蘭登所說,最好創建某種網格以減少實際檢測到的碰撞次數。

如果您確實想要最大的性能,我建議使用普通的javascript而不是jQuery,但這是我創建的jQuery解決方案。

 var gridDimensions = { x: 800, y: 800 }; var boxDimensions = { x: 80, y: 80 }; var hashes = hashSquares($('.square'), gridDimensions, boxDimensions); function hashSquares($squares, dimensions, squaresDimensions) { var squaresHash = []; for (var i = 0; i < Math.floor(dimensions.x / squaresDimensions.x); i++) { var yHashes = Array(Math.floor(dimensions.y / squaresDimensions.y)); for (var j = 0; j < yHashes.length; j++) { yHashes[j] = []; } squaresHash.push(yHashes); } $squares.each(function() { var $this = $(this); squaresHash[Math.floor($this.position().left / squaresDimensions.x)][Math.floor($this.position().top / squaresDimensions.y)].push($this); }); return squaresHash; } function checkSameSquare(x, y, hash) { //if they are both in the same hash square they definitely overlap if (hash[x][y].length > 1) { $.each(hash[x][y], function(i, $el) { //skip the first element if (i !== 0) { $el.addClass('collided'); } }); } } function checkSquareBelow(x, y, hash) { $.each(hash[x][y], function(i, $el) { $.each(hash[x][y + 1], function(i2, $el2) { if (detectCollision($el, $el2)) { $el2.addClass('collided'); } }); }); } function checkSquareRight(x, y, hash) { $.each(hash[x][y], function(i, $el) { $.each(hash[x + 1][y], function(i2, $el2) { if (detectCollision($el, $el2)) { $el2.addClass('collided'); } }); }); } function checkSquareDiagonalRightBelow(x, y, hash) { $.each(hash[x][y], function(i, $el) { $.each(hash[x + 1][y + 1], function(i2, $el2) { if (detectCollision($el, $el2)) { $el2.addClass('collided'); } }); }); } function detectCollision($div1, $div2) { var x1 = $div1.offset().left; var y1 = $div1.offset().top; var h1 = $div1.outerHeight(true); var w1 = $div1.outerWidth(true); var b1 = y1 + h1; var r1 = x1 + w1; var x2 = $div2.offset().left; var y2 = $div2.offset().top; var h2 = $div2.outerHeight(true); var w2 = $div2.outerWidth(true); var b2 = y2 + h2; var r2 = x2 + w2; if (b1 < y2 || y1 > b2 || r1 < x2 || x1 > r2) return false; return true; } for (var i = 0; i < hashes.length; i++) { for (var j = 0; j < hashes[i].length; j++) { checkSameSquare(j, i, hashes); if (j < hashes[i].length - 1) { checkSquareRight(j, i, hashes); } if (i < hashes.length - 1) { checkSquareBelow(j, i, hashes); } if (j < hashes[i].length - 1 && i < hashes.length - 1) { checkSquareDiagonalRightBelow(j, i, hashes); } } } 
 body { margin: 10px; font-family: Arial, sans-serif; } #container { background-color: #cccccc; height: 880px; position: relative; width: 880px; } .square { background-color: lawngreen; height: 80px; position: absolute; width: 80px; z-index: 10; } .square > div { font-size: 12px; padding: 5px; } .square:hover { background-color: forestgreen; z-index: 11; cursor: pointer; } .collided { background-color: red; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="container"> <div id="square_1" class="square" style="top: 31px; left: 141px;"> <div>square_1</div> </div> <div id="square_2" class="square" style="top: 56px; left: 726px;"> <div>square_2</div> </div> <div id="square_3" class="square" style="top: 555px; left: 391px;"> <div>square_3</div> </div> <div id="square_4" class="square" style="top: 725px; left: 330px;"> <div>square_4</div> </div> <div id="square_5" class="square" style="top: 398px; left: 642px;"> <div>square_5</div> </div> <div id="square_6" class="square" style="top: 642px; left: 794px;"> <div>square_6</div> </div> <div id="square_7" class="square" style="top: 521px; left: 187px;"> <div>square_7</div> </div> <div id="square_8" class="square" style="top: 621px; left: 455px;"> <div>square_8</div> </div> <div id="square_9" class="square" style="top: 31px; left: 549px;"> <div>square_9</div> </div> <div id="square_10" class="square" style="top: 677px; left: 565px;"> <div>square_10</div> </div> <div id="square_11" class="square" style="top: 367px; left: 120px;"> <div>square_11</div> </div> <div id="square_12" class="square" style="top: 536px; left: 627px;"> <div>square_12</div> </div> <div id="square_13" class="square" style="top: 691px; left: 312px;"> <div>square_13</div> </div> <div id="square_14" class="square" style="top: 93px; left: 757px;"> <div>square_14</div> </div> <div id="square_15" class="square" style="top: 507px; left: 720px;"> <div>square_15</div> </div> <div id="square_16" class="square" style="top: 251px; left: 539px;"> <div>square_16</div> </div> </div> 

http://jsfiddle.net/81y70h1f/13/

請注意,您只需要在與右下角右下方和右下角的正方形相同的正方形上測試碰撞,因為在沿着網格移動時已經處理了所有其他碰撞。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM