<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## 碰撞檢測 碰撞檢測是物體與物體之間的交互,其實在前面的邊界檢測也是一種碰撞檢測,只不過檢測的對象是物體與邊界之間。在本章中,我們將介紹更多的碰撞檢測,比如:兩個物體間的碰撞檢測、一個物體與一個點的碰撞檢測、基于距離的碰撞檢測等等碰撞檢測方法。 **什么是碰撞檢測呢?** 簡單來說,碰撞檢測就是判定兩個物體是否在同一時間內占用一塊空間,用數學的角度來看,就是兩個物體有沒有交集。 檢測碰撞的方法有很多,一般我們使用如下兩種: 從幾何圖形的角度來檢測,就是判斷一個物體是否與另一個有重疊,我們可以用物體的矩形邊界來判斷。 檢測距離,就是判斷兩個物體是否足夠近到發生碰撞,需要計算距離和判斷兩個物體是否足夠近。 **1、基于幾何圖形的碰撞檢測** 基于幾何圖形的碰撞檢測,一般情況下是檢查一個矩形是否與其他矩形相交,或者某一個坐標點是否落在矩形內。 **1.1 兩個物體間的碰撞檢測(矩形邊界檢測法)** 在上一章中,我們介紹了一個 getBound() 方法,參數為球對象,返回矩形對象。 ``` function getBound(body){ return { x: (body.x - body.radius), y: (body.y - body.radius), width: body.radius * 2, height: body.radius * 2 }; } ``` 現在我們已經知道如何獲取物體的矩形邊界,那么只需檢測兩個對象的邊界框是否相交,就可以判斷兩個物體是否碰撞了。我們在 tool.js 工具類中添加一個工具函數 tool.intersects : ``` tool.intersects = function(bodyA,bodyB){ return !(bodyA.x + bodyA.width < bodyB.x || bodyB.x + bodyB.width < bodyA.x || bodyA.y + bodyA.height < bodyB.y || bodyB.y + bodyB.height < bodyA.y); }; ``` 這個函數傳入兩個矩形對象,如果返回true,表示兩個矩形相交了;否則,返回false。(如果你看不明白這段代碼,請看下圖,讓一個矩形分別位于另一個矩形的上下左右位置): ![](https://box.kancloud.cn/fbb5ac590132efc02e78399a8d96fdad_388x314.jpg) 檢測函數已經知道了,當要檢測兩個物體是否相交時,就可以做如下判斷: ``` if (tool.intersects(objectA,objectB)) { console.log('撞上了'); } ``` 注意:這里傳入的必須是矩形對象。如果是球,可調用getBound()方法返回矩形對象。如果已經是矩形對象,就直接傳入。 這里有一個需要注意的問題,有些時候,我們的物體是不規則的,如果我們采取矩形邊界檢測,有時候會不精確(只有真正的矩形才是精確的): ![](https://box.kancloud.cn/598a7c9e6e01e4a95419490a72412494_600x195.jpg) 在上面的圖中,有矩形、圓形和五角形,我們都可以采取矩形邊界檢測法,不過,你會發現,當物體是不規則的形狀時,雖然通過上面的 tool.intesects() 方法判斷兩個物體已經碰撞,但實際上并沒有,所以矩形邊界檢測法對不規則的圖形來說,這只是一種不精確的檢測方法,如果你要精確檢測,那就要做更多的檢測了。當然,矩形邊界檢測法對于大多數情況下已經足夠了。 實例又來了(用iframe插入會導致頁面卡,所以放在單獨頁面中,點擊可看):http://ghmagical.com/Iframe/show/code/intersect ``` if(activeRect !== rect && tool.intersects(activeRect, rect)) { activeRect.y = rect.y - activeRect.height; activeRect = createRect(); }; ``` 這個例子是不是有點像俄羅斯方塊呢,每一次只有一個活動物體,然后循環檢測它是否與已經存在的物體碰撞,如果碰撞,則將活動物體放在與它碰撞物體的上面,然后創建一個新的方塊。 **1.2 物體與點的碰撞檢測** 在前面我們在 tool工具類中添加了一個工具函數 tool.containsPoint,它接受三個參數,第一個是矩形對象,后面兩個是一個點的x和y的坐標,返回值是true或false: ``` tool.containsPoint = function(body, x, y){ return !(x < body.x || x > (body.x + body.width) || y < body.y || y > (body.y + body.height)); }; ``` 其實,tool.containsPoint()函數就是在檢測點與矩形是否碰撞。 比如,要檢測點(50,50)是否在一個矩形內: ``` if(tool.containsPoint(body,50,50)){ console.log('在矩形內'); } ``` tool.intesects()和tool.containsPoint()方法都會遇到精確問題,對矩形最精確,越不規則,精確率就越小。大多數情況下,都會采取這兩種方法。當然,如果你要對不規則圖形采取更精確的方法,那你就要寫更多的代碼去執行精確的檢測了。 **2、基于距離的碰撞檢測** 距離就是指兩個物體間的距離,當然,物體總是有高寬的,這就還要考慮高寬。一般我們會先確定兩個物體的最小距離,然后計算當前距離,最后進行比較,如果當前距離比最小距離小,那肯定發生了碰撞。 這種距離檢測法,對圓來說是最精確的,而對于其他圖形,或多或少會有一些精確問題。 **2.1 基于距離的簡單碰撞檢測** 基于距離的碰撞檢測的最理想的情況是:有兩個正圓形要進行碰撞檢測,從圓的中心點開始計算。 要檢測兩個圓是否碰撞,其實就是比較兩個圓的中心點的距離與兩個圓的半徑和的大小關系。 ``` dx = ballB.x - ballA.x; dy = ballB.y - ballA.y; dist = Math.sqrt(dx * dx + dy * dy); if(dist < ballA.radius + ballB.radius){ console.log('碰撞了'); } ``` 實例:canvas-demo/distanceIntersect.html 在上面的例子中,碰撞距離就是一個球的半徑加上另一個球的半徑,也是碰撞的最小距離,而兩者真正的距離就是圓心與圓心的距離。 ``` var dx = ballB.x - ballA.x; var dy = ballB.y - ballA.y; var dist = Math.sqrt(dx * dx + dy * dy); if(ball != ballB && dist < ballA.radius + ballB.radius){ ctx.strokeStyle = 'red'; var txt = '你壓著我了'; var tx = ballA.x - ctx.measureText(txt).width / 2; ctx.font = '30px Arial' ctx.strokeText(txt,tx,ballA.y); }; ``` **2.2 彈性碰撞** 就像2.1節里的例子一樣,當兩個球碰撞時,我們加入了文字提示,當然,我們還可以做更多操作,比如這節要講的彈性碰撞。 實例:canvas-demo/springIntersect.html 首先我們加入一個放在canvas中心的圓球ballA,然后加入多個隨機大小和隨機速度的圓球,讓它們做勻速運動,遇到墻就反彈,最后在每一幀使用基于距離的方法檢測小球是否與中央的圓球ballA發生了碰撞,如果發生了碰撞,則計算彈動目標點和兩球間的最小距離來避免小球完全撞上圓球ballA。 對于小球和圓球ballA的碰撞,我們可以這樣理解,我們在ballA外設置了目標點,然后讓小球向目標點彈動,一旦小球到達目標點,就不再繼續碰撞,彈性運動就結束了,繼續做勻速運動。 下面的效果就像一群小氣泡在大氣泡上反彈,小氣泡撞入大氣泡一點距離,這個距離取決于小氣泡的速度,然后被彈出來。 如果你看不懂它如何反彈的,那你就要回到上一章看看《緩動和彈動》是如何實現的了。 **3、多物體的碰撞檢測策略** 這一節并不會介紹新的碰撞檢測方法,而是介紹如何優化多物體碰撞代碼。 如果你用過二維數組,那么你肯定知道如何去遍歷數組元素,通常的方法是使用兩個循環函數,而多物體的碰撞檢測,也類似二維數組: ``` for(var i = 0; i < objects.length; i++){ var objectA = objects[i]; for(var j = 0; j < objects.length; j++){ var objectB = objects[j]; if(tool.intersects(objectA,objectB){} } }; ``` 上面的方法的語法是沒錯的,不過這段代碼有兩個效率問題: **(1)多余的自身碰撞檢測** 它檢測了同一個物體是否自身碰撞,比如:第一個物體(i=0)是objects[0],在第二次循環中,第一個物體(j=0)也是objects[0],是不是完全沒必要的檢測,我們可以這樣避免: ``` if(i != j && tool.intersects(objectA,objectB){} ``` 這樣會節省了i次碰撞檢測 **(2)重復碰撞檢測** 第一次(i=0)循環時,我們檢測了objects[0](i=0)和objects[1](j=1)的碰撞;第二次(i=1)循環時,代碼似乎又檢測了objects[1](i=1)和objects[0](j=0)的碰撞,這豈不是多余的嗎? 我們應該做如下的避免: ``` for(var i = 0; i < objects.length; i++){ var objectA = objects[i]; for(var j = i + 1; j < objects.length; j++){ var objectB = objects[j]; if(tool.intersects(objectA,objectB){} } }; ``` 這樣處理后,不僅避免了自身碰撞檢測,而且減少了重復碰撞檢測。 實例:canvas-demo/collision.html 在上面的例子中,兩個球在碰撞后的彈動代碼并沒有太大的區別,只不過這里將ballB當成了中央位置的圓球而已: ``` function checkCollision(ballA, ballB) { var dx = ballA.x - ballB.x; var dy = ballA.y - ballB.y; var dist = Math.sqrt(dx * dx + dy * dy); var min_dist = ballB.radius + ballA.radius; if(dist < min_dist) { var angle = Math.atan2(dy, dx); var tx = ballB.x + Math.cos(angle) * min_dist; var ty = ballB.y + Math.sin(angle) * min_dist; var ax = (tx - ballA.x) * spring * 0.5; var ay = (ty - ballA.y) * spring * 0.5; ballA.vx += ax; ballA.vy += ay; ballB.vx += (-ax); ballB.vy += (-ay); }; }; ``` 上面代碼最后四行的意思是:不僅ballB要從ballA彈開,而且ballA要從ballB彈出,它們的加速度的絕對值是相同的,方向相反。 不知道你有沒有注意到,ax和ay的計算都乘以0.5,這是因為當ballA移動ax時,ballB也反向移動ax,那么就造成了 ax 變成 2ax ,所以要乘以0.5,才是真正的加速度。當然,你也可以將spring減小成原來的一半。 **總結** 碰撞檢測是很多動畫中必不可少的,你必須掌握基于幾何圖形的碰撞檢測、基于距離的碰撞檢測方法,以及如何更有效的的檢測多物體間的碰撞。 **附錄** **重要公式:** (1)矩形邊界碰撞檢測 ``` tool.intersects = function(bodyA,bodyB){ return !(bodyA.x + bodyA.width < bodyB.x || bodyB.x + bodyB.width < bodyA.x || bodyA.y + bodyA.height < bodyB.y || bodyB.y + bodyB.height < bodyA.y); }; ``` (2)基于距離的碰撞檢測 ``` dx = objectB.x - objectA.x; dy = objectB.y - objectA.y; dist = Math.sqrt(dx * dx + dy * dy); if(dist < objectA.radius + objectB.radius){} ``` (3)多物體碰撞檢測 ``` for(var i = 0; i < objects.length; i++){ var objectA = objects[i]; for(var j = i + 1; j < objects.length; j++){ var objectB = objects[j]; if(tool.intersects(objectA,objectB){} } }; ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看