1. 程式人生 > >碼農乾貨系列【1】--方向包圍盒(OBB)碰撞檢測

碼農乾貨系列【1】--方向包圍盒(OBB)碰撞檢測

乾貨

最近一直在刪文章,不是要關博洗手什麼的,而是被刪的文章沒有達到“乾貨”的標準。乾貨的反義詞是水貨,比如我們經常吃的注水豬肉,它就是水貨,非乾貨。什麼是“乾貨”。?經過一番搜尋,標準的描述是:實用性比較強的,不含任何吹噓水分,也沒有虛假的成分,所以業內人士通常把這一類分享活動稱之為“乾貨”。

文章是否是乾貨做如下幾點要求:

必備條件:

1.整體樣式風格整齊美觀;

2.實用性比較強的,邏輯條理清晰;

3.獨立性強,一篇文章只寫一類東西;

4.碼農看完就懂(或者有了search的方向),拿去就能用;

加精條件:

1.圖文並茂;

2.線上演示;

3.示例程式碼下載;

從這篇開始我們的乾貨之旅~~~~~

簡介

包圍體是一個簡單的幾何空間,裡面包含著複雜形狀的物體。為物體新增包圍體的目的是快速的進行碰撞檢測或者進行精確的碰撞檢測之前進行過濾(即當包圍體碰撞,才進行精確碰撞檢測和處理)。包圍體型別包括球體、軸對齊包圍盒(AABB)、有向包圍盒(OBB)、8-DOP以及凸殼。如圖1所示。

clip_image001

圖1 依次是球體、AABB、OBB

可以看到圖1是3D包圍體,在2D包圍體如圖2所示:
clip_image003

圖2 依次是球體、AABB、OBB

OBB

方向包圍盒(Oriented bounding box),簡稱OBB。方向包圍盒類似於AABB,但是具有方向性、可以旋轉,AABB不能旋轉。如圖3所示。

clip_image005

圖3 矩形和矩形投影檢測的四條軸

要計算兩個OBB是否碰撞,只需要計算他們在圖3上的4個座標軸上的投影是否有重疊,如果有,則兩多邊形有接觸。這也可以擴充套件到任意多邊形,如圖4所示。

clip_image007

圖4 矩形和三角形投影檢測的五條軸

投影軸來自於多邊形自身邊的垂線。

判定方式:兩個多邊形在所有軸上的投影都發生重疊,則判定為碰撞;否則,沒有發生碰撞

OBB存在多種的表達方式,這裡使用最常用的一種:一箇中心點、2個矩形的邊長、兩個旋轉軸(該軸垂直於多邊形自身的邊,用於投影計算)。程式碼如下所示:

(function (window) {

    var OBB = function (centerPoint, width, height, rotation) {

        this
.centerPoint = centerPoint; this.extents = [width / 2, height / 2]; this.axes = [new Vector2(Math.cos(rotation), Math.sin(rotation)), new Vector2(-1 * Math.sin(rotation), Math.cos(rotation))]; this._width = width; this._height = height; this._rotation = rotation; } window.OBB = OBB; })(window);

其所依賴的Vector2這個類如下所示:

(function (window) {
    Vector2 = function (x, y) {
        this.x = x || 0;
        this.y = y || 0;
    };

    Vector2.prototype = {
        sub: function (v) {
            return new Vector2(this.x - v.x, this.y - v.y)
        },
        dot: function (v) {
            return this.x * v.x + this.y * v.y;
        }
    };
    window.Vector2 = Vector2;
} (window))

然後基於這個資料結構,進行OBB之間的相交測試。為OBB擴充套件一個方法,即或者在任意軸上的投影半徑:

OBB.prototype = {
    getProjectionRadius: function (axis) {
        returnthis.extents[0] * Math.abs(axis.dot(this.axes[0])) + this.extents[1] * Math.abs(axis.dot(this.axes[1]));
    }
}

這裡你可能需要讀者瞭解Vector2.dot的幾何意義:若b為單位向量,則a與b的點積即為a在方向b的投影

有了這些,就可以進行相交檢測。由上面的判定方式,可以得出,兩個矩形之間的碰撞檢測需要判斷四次(每個投影軸一次)。完整檢測程式碼如下所示:

(function (window) {

    var CollisionDetector = {

        detectorOBBvsOBB: function (OBB1, OBB2) {
            var nv = OBB1.centerPoint.sub(OBB2.centerPoint);
            var axisA1 = OBB1.axes[0];
            if (OBB1.getProjectionRadius(axisA1) + OBB2.getProjectionRadius(axisA1) <= Math.abs(nv.dot(axisA1))) return false;
            var axisA2 = OBB1.axes[1];
            if (OBB1.getProjectionRadius(axisA2) + OBB2.getProjectionRadius(axisA2) <= Math.abs(nv.dot(axisA2))) return false;
            var axisB1 = OBB2.axes[0];
            if (OBB1.getProjectionRadius(axisB1) + OBB2.getProjectionRadius(axisB1) <= Math.abs(nv.dot(axisB1))) return false;
            var axisB2 = OBB2.axes[1];
            if (OBB1.getProjectionRadius(axisB2) + OBB2.getProjectionRadius(axisB2) <= Math.abs(nv.dot(axisB2))) return false;
            return true;

        }
    }

    window.CollisionDetector = CollisionDetector;
})(window)

這裡拿兩個OBB的中心點連線在座標軸上的投影長度和兩個矩形投影半徑之和進行對比,如果半徑之後都小於或者等於中心連線之後才判定為碰撞,否則判定為分離狀態。

整合圖形化測試介面

為了更加直觀的測試OBB碰撞檢測方法,使用Easeljs輸出碰撞的狀態。當兩個矩形沒有發生碰撞的時候,兩矩形呈現藍色;當兩個矩形發生碰撞的時候,兩矩形呈現紅色。先引入相關的指令碼庫以及用於顯示的canvas畫布:

<script src="Vector2.js" type="text/javascript"></script>
<script src="OBB.js" type="text/javascript"></script>
<script src="CollisionDetector.js" type="text/javascript"></script>
<script src="easel.js" type="text/javascript"></script>
<canvas id="testCanvas" width="980" height="580">

然後進行OBB初始化以及碰撞檢測:

var OBB1, OBB1x = 100, OBB1y = 150, OBB1w = 30, OBB1h = 140, OBB1r = 30;
var OBB2, OBB2x = 100, OBB2y = 70, OBB2w = 40, OBB2h = 110, OBB2r = 40;
var canvas;
var stage;
var color;

function init() {

    canvas = document.getElementById("testCanvas");
    stage = new Stage(canvas);

    Ticker.addListener(window);
}
     
function tick() {
    stage.removeAllChildren();

    OBB1r += 2;
    OBB2r += 1;
    OBB1 = new OBB(new Vector2(OBB1x, OBB1y), OBB1w, OBB1h, OBB1r * Math.PI / 180);
    OBB2 = new OBB(new Vector2(OBB2x, OBB2y), OBB2w, OBB2h, OBB2r * Math.PI / 180);
    var r = CollisionDetector.detectorOBBvsOBB(OBB1, OBB2);

    color=r?"red":"#00F";
    OBB1 = new Container();
    stage.addChild(OBB1);
    OBB1.x = OBB1x;
    OBB1.y = OBB1y;
    var frame1 = new Shape();
    frame1.graphics.beginFill(color).drawRect(0, 0, OBB1w, OBB1h);
    frame1.rotation = OBB1r;
    frame1.regX = OBB1w / 2;
    frame1.regY = OBB1h / 2;
    OBB1.addChild(frame1);

    OBB2 = new Container();
    stage.addChild(OBB2);
    OBB2.x = OBB2x;
    OBB2.y = OBB2y;
    var frame2 = new Shape();
    frame2.graphics.beginFill(color).drawRect(0, 0, OBB2w, OBB2h);
    frame2.rotation = OBB2r;
    frame2.regX = OBB2w / 2;
    frame2.regY = OBB2h / 2;
    OBB2.addChild(frame2);

    stage.update();
}
init();

以上程式碼定義了兩個旋轉的OBB包圍盒,當他們發生碰撞則改變繪製的顏色,使其成為紅色。執行程式碼,效果圖5和6所示。

clip_image009

圖5 未發生碰撞

clip_image011

圖6 發生碰撞

這裡是2D情況下的OBB碰撞檢測,對於3D OBB碰撞檢測,更為複雜。需要測試15個分離軸以確定OBB的相交狀態,兩個OBB的座標軸各3個,以及垂直於每個軸的9個軸。除了座標軸個數不一樣,其相交測試思路和本文一致,本文不再探討。

線上演示

更多幹貨敬請期待~~~~~

如果您覺得此文有幫助,可以打賞點錢給我支付寶[email protected] ,或掃描二維碼

相關推薦

乾貨系列1--方向包圍(OBB)碰撞檢測

乾貨 最近一直在刪文章,不是要關博洗手什麼的,而是被刪的文章沒有達到“乾貨”的標準。乾貨的反義詞是水貨,比如我們經常吃的注水豬肉,它就是水貨,非乾貨。什麼是“乾貨”。?經過一番搜尋,標準的描述是:實用性比較強的,不含任何吹噓水分,也沒有虛假的成分,所以業內人士通常把這一類分享活動稱之為“乾貨”。 文章是否

Android自己定義組件系列1——自己定義View及ViewGroup

全部 int ++ btn -i pre 剪切 final 界面 View類是ViewGroup的父類,ViewGroup具有View的全部特性。ViewGroup主要用來充當View的容器。將當中的View作為自己孩子,並對其進行管理。當然孩子也能夠是ViewGrou

Web開發系列1實用的網頁佈局(PC端)

在熟悉那些常用的軟體、工具後,我們正式開始開發,在前期準備工作之後,我們要做的事情是寫頁面,也就是網頁佈局。在w3c、菜鳥、慕課網等等網站上都有基礎的 HTML+CSS 知識講解,在初期學習中,跟著教程全部過一遍就差不多了。剛開始寫頁面的時候我們會迷糊,那麼多的標籤

循序漸進學.Net Core Web Api開發系列1:開發環境

系列目錄 一、本篇概述  本篇不打算描述如何通過Visual Studio建立一個專案之類的話題,主要描述以下內容: 1、使用NuGet和Bower引入第三方庫 2、Linux下安裝執行環境 3、關於安裝虛擬機器時碰到的網路設定的問題 實驗環境:Windows 10 ,Visual S

Android中的動畫詳解系列1——逐幀動畫

逐幀動畫其實很簡單,下面我們來看一個例子:<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:andro

你不知道的JS系列1- 什麼是作用域

  幾乎所有的程式語言都能夠儲存變數,並且能在之後對這個變數值進行訪問或修改,正是儲存和訪問變數的能力將狀態帶給了程式,那麼,這些變數儲存在哪裡呢?程式需要時又是如何找到他們?這些問題說明需要一套設計良好的規則來儲存變數,並且之後可以方便的找到這些變數,這套規則被稱為作用域。 1、瞭解編譯原理   儘管將JS

1000行代徒手寫正則表達式引擎1--JAVA中正則表達式的使用

基礎上 unicode 要求 [1] 分配 find 通過 images char 簡介: 本文是系列博客的第一篇,主要講解和分析正則表達式規則以及JAVA中原生正則表達式引擎的使用。在後續的文章中會涉及基於NFA的正則表達式引擎內部的工作原理,並在此基礎上用1000行左右

FFmpeg入門--流 / 位元速率 / 位元率 / 幀速率 / 解析度 1

I、P、B 幀 I 幀(Intracoded frames):I 幀影象採用幀內編碼方式,即只利用了單幀影象內的空間相關性,而沒有利用時間相關性。I 幀使用幀內壓縮,不使用運動補償,由於I 幀不依賴其它幀,所以是隨機存取的入點,同時是解碼的基準幀。I 幀主要用於接收機的初始化和通

零基礎學python,看完這篇文章,你的python基礎就差不多了!乾貨1

本文分為兩部分: Python基礎語法和麵向物件(下一篇分享面向物件)   Python基礎語法   1. 認識Python 1.1 Python 簡介 Python 的創始人為吉多·範羅蘇姆(Guido van Rossum)。

netty研究1:編譯源代

12.1 code github上 ade you 很多 recycle dex pass netty研究【1】:編譯源代碼 netty作為異步通信底層框架,其優異的性能讓我產生了研究他的源碼的決定。 代碼研究之前,第一步就是要準備環境,至少可以編譯通過,下面,

MySQL系列--優化1——巢狀join優化

表達連線的語法允許巢狀連線。以下討論參見第13.2.9.2節“join語法”中描述的連線語法。 與SQL標準相比,table_factor的語法被擴充套件。後者僅接受table_reference,而不是一對括號內的列表。如果我們將table_referenc

Http淺析1——流程|請求頭|響應頭|狀態

本文如有疏漏,後續將繼續補充。 HTTP知識 Http工作的基本流程 (1)http客戶端發起請求,建立埠; (2)http伺服器在埠監聽客戶端 請求; (3)http伺服器向客戶端返回狀態和內容; 當我們在瀏覽器中輸入一個網址時,瀏覽器和伺

CANopen系列文章1--SYNC4-同步RPDO & 同步TPDO

在本論壇,有網友說:同步RPDO是由主站傳送給從站的PDO,主站在傳送SYNC之前,將所有從站的RPDO傳送給從站,然後再發SYNC,此時所有從站同時處理此RPDO。 然後遠方大俠同意這個觀點。 我的

D3.js 進階系列 — 2.1 力學圖的事件 + 頂點的固定

本章討論在力學圖中常用到的事件( Event ),然後對【進階 - 第 2.0 章】的人物關係圖進行改進,使使用者能夠固定拖拽的物件。 force.on("tick", function(){ });這裡的 force 是之前程式碼中定義的佈局( Layout )

LeetCode系列Easy1 Algorithms

2016.10.09啟程。。 Algorithms Bit Manipulation 190. Reverse Bits Leetcode 題目連結 Reverse bits of a given 32 bits unsi

Dr.Elephant原始碼分析系列文章-1

Dr.Elephant是LinkedIn於2016年4月開源的一個Hadoop平臺效能調優工具。我們可以從這個連結獲取原始碼:https://github.com/linkedin/dr-elepha

CANopen系列文章1--SYNC2

1】 【遠方】同步RPDO是由主站傳送給從站的PDO,主站在傳送SYNC之前,將所有從站的RPDO傳送給從站,然後再發SYNC,此時所有從站同時處理此RPDO。 每個從站可以有不同的RPDO,以COB

大戰設計模式(第二季)1———— 從源看工廠模式

new map 下一個 建議 mage 管理 增長 如果 per 前言 工廠模式其實在許多地方都有體現,是常見的一種設計模式。用一句話總結就是,當我們需要創建一些對象的時候,而創建的對象比較復雜或者同類型比較多,就可以使用它。 在我們看源碼的時候,有的時候看見XXXFa

JVM系列6GC與調優1

### JVM系列筆記目錄 > - 虛擬機器的基礎概念 > - class檔案結構 > - class檔案載入過程 > - jvm記憶體模型 > - JVM常用指令 > - GC與調優 ### GC基礎知識 - #### 什麼是垃圾 ​ 沒有任何引用指向的一個

Unity3d修煉之路:遊戲開發中,3d數學知識的練習1(不斷更新.......)

turn tor rdo pre 長度 scrip 縮放 unity3d float #pragma strict public var m_pA : Vector3 = new Vector3(2.0f, 4.0f, 0.0f); public var m_pB :