cocos2d-xAABB碰撞檢測

1.AABB包圍盒

在遊戲中,爲了簡化物體之間的碰撞檢測運算,通常會對物體創建一個規則的幾何外形將其包圍。

其中,AABB(axis-alignedboundingbox)包圍盒被稱爲軸對其包圍盒


二維場景中的AABB包圍盒具備特點:(注:由於Cocos2d-x是基於OpenglES的,所以下圖中的所有座標系均採用右手直角座標系)

(1)表現形式爲四邊形,即用四邊形包圍物體。

(2)四邊形的每一條邊,都會與座標系的軸垂直。

如圖1-1所示:

1411694363950725.png

圖1-1


三維場景中的AABB包圍盒特點:

(1)表現形式爲六面體。

(2)六面體中的每條邊都平行於一個座標平面。

如圖1-2所示:

1411694505408887.jpg

圖1-2(圖片來源百度)

在圖1-2中,爲了更明顯的展示AABB包圍盒的特點,在最右側展示了一個OBB(OrientedBoundingBox)包圍盒,也稱作有向包圍盒。

可以看出,AABB包圍盒與OBB包圍盒的最直接的區別就是,AABB包圍盒是不可以旋轉的,而OBB包圍盒是可以旋轉的,也就是有向的。


2.二維場景中的AABB碰撞檢測原理

首先來看一張二維場景中的物體碰撞圖:

1411694610939934.jpg

圖2-1

在圖2-1中,分別做物體A與物體B在X,Y軸方向的投影,物體A的Y軸方向最大點座標爲Y1,最小點座標Y2,X軸方向最小點座標X1,最大點座標X2,物體B同理。圖中紅色區域爲物體A與物體B投影的重疊部分。


可以看出,AABB碰撞檢測具有如下規則:

物體A與物體B分別沿兩個座標軸做投影,只有在兩個座標軸都發生重疊的情況下,兩個物體才意味着發生了碰撞。

所以,在程序中做二維遊戲的AABB碰撞檢測時,只需驗證物體A與物體B是否滿足如下條件:

(1)物體A的Y軸方向最小值大於物體B的Y軸方向最大值;

(2)物體A的X軸方向最小值大於物體B的X軸方向最大值;

(3)物體B的Y軸方向最小值大於物體A的Y軸方向最大值;

(4)物體B的X軸方向最小值大於物體A的X軸方向最大值;

若滿足上述條件,則證明物體A與物體B並未發生重合,反之,則證明物體A與物體B重合。


3.三維場景中的AABB碰撞檢測原理

首先,再來看一下圖2-1中的二維物體A和物體B的包圍盒,可以發現實際上判斷物體A與物體B是否發生重合只需要知道兩個信息:

(1)物體A的最小點的信息,即圖2-1中A的左下角點;以及物體A的最大點的信息,即圖2-1中A的右上角點。

(2)物體B的最小點的信息,物體B的最大點的信息。

也就是說在二維場景的碰撞檢測中,每個物體的頂點座標信息都可以由兩個座標來確定,即兩個座標就可以標識一個物體了,所以兩個物體的碰撞檢測只需要獲得到四個點座標就可以了。


之前在圖1-2中已經看到,三維場景中物體的AABB包圍盒是一個六面體,其座標系對於二維座標系來講只是多了一個Z軸,所以實際上在三維場景中物體的AABB碰撞檢測依然可以採用四個點信息的判定來實現。即從物體A的八個頂點與物體B的八個頂點分別選出兩個最大與最小的頂點進行對比。三維物體的AABB包圍盒的八個頂點依舊可以用兩個頂點來標識,如圖3-1所示:

20140925170833234.png

圖3-1

只要確定了圖中黑色點部分的座標,就可以確定八個頂點的全部信息了。


在Cocos2d-x3.x版本中,爲開發者提供了AABB類,用於保存包圍盒的最大頂點與最小頂點的信息,並且爲每個Sprite3D對象提供了獲取AABB包圍盒的接口,在AABB類同時提供了判斷相應的碰撞檢測的方法。


下面對AABB的源碼進行分析:

CCAABB.h文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class CC_3D_DLL AABB
{
 
public:
    /**
     * 構造函數
     */
    AABB();
     
    /**
     * 構造函數 參數:最小頂點座標,最大頂點座標
     */
    AABB(const Vec3& min, const Vec3& max);
     
    /**
     * 構造函數 參數:AABB包圍盒
     */
    AABB(const AABB& box);
     
    /**
     * 獲取包圍盒中心點座標
     */
    Vec3 getCenter();
     
    /* 獲取包圍盒八個頂點信息
     * Z軸正方向的面
     * verts[0] : 左上頂點
     * verts[1] : 左下頂點
     * verts[2] : 右下頂點
     * verts[3] : 右上頂點
     *
     * Z軸負方向的面
     * verts[4] : 右上頂點
     * verts[5] : 右下頂點
     * verts[6] : 左下頂點
     * verts[7] : 左上頂點
     */
    void getCorners(Vec3 *dst) const;
 
    /**
     * 判斷兩個包圍盒是否重合
     */
    bool intersects(const AABB& aabb) const;
 
    /**
     * 判斷一個點是否在包圍盒內
     */
    bool containPoint(const Vec3& point) const;
 
    /**
     由兩個包圍盒生成一個能同時包圍這兩個包圍盒的最小包圍盒
     */
    void merge(const AABB& box);
 
    /**
     * 設置包圍盒的最大頂點與最小頂點
     */
    void set(const Vec3& min, const Vec3& max);
     
    /**
     * 復位函數 初始化最大最小頂點信息
     */
    void reset();
     
    bool isEmpty() const;
 
    /**
     * 更新最大頂點與最小頂點信息
     */
    void updateMinMax(const Vec3* point, ssize_t num);
     
    /**
     * 由一個矩陣對對包圍盒進行頂點變換
     */
    void transform(const Mat4& mat);
 
public:
    Vec3 _min;   //三維向量 保存最小點座標
    Vec3 _max;   //三維向量 保存最大點座標
};
 
NS_CC_END


CCAABB.cpp文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#include "3d/CCAABB.h"
 
NS_CC_BEGIN
 
//構造函數
AABB::AABB()
{
    reset(); //初始化最大頂點與最小頂點
}
 
AABB::AABB(const Vec3& min, const Vec3& max)
{
    set(min, max); //設置最大頂點與最小頂點
}
 
AABB::AABB(const AABB& box)
{
    set(box._min,box._max); //設置最大頂點與最小頂點
}
//獲取包圍盒中心點座標
Vec3 AABB::getCenter()
{
    Vec3 center;
    center.x = 0.5f*(_min.x+_max.x);
    center.y = 0.5f*(_min.y+_max.y);
    center.z = 0.5f*(_min.z+_max.z);
 
    return center;
}
//獲取包圍盒八個頂點信息
void AABB::getCorners(Vec3 *dst) const
{
    assert(dst);
     
    // 朝着Z軸正方向的面
    // 左上頂點座標
    dst[0].set(_min.x, _max.y, _max.z);
    // 左下頂點座標
    dst[1].set(_min.x, _min.y, _max.z);
    // 右下頂點座標
    dst[2].set(_max.x, _min.y, _max.z);
    // 右上頂點座標
    dst[3].set(_max.x, _max.y, _max.z);
 
    // 朝着Z軸負方向的面
    // 右上頂點座標
    dst[4].set(_max.x, _max.y, _min.z);
    // 右下頂點座標
    dst[5].set(_max.x, _min.y, _min.z);
    // 左下頂點座標
    dst[6].set(_min.x, _min.y, _min.z);
    // 左上頂點座標
    dst[7].set(_min.x, _max.y, _min.z);
}
//判斷兩個包圍盒是否碰撞
bool AABB::intersects(const AABB& aabb) const
{
    return ((_min.x >= aabb._min.x && _min.x <= aabb._max.x) || (aabb._min.x >= _min.x && aabb._min.x <= _max.x)) &&
           ((_min.y >= aabb._min.y && _min.y <= aabb._max.y) || (aabb._min.y >= _min.y && aabb._min.y <= _max.y)) &&
           ((_min.z >= aabb._min.z && _min.z <= aabb._max.z) || (aabb._min.z >= _min.z && aabb._min.z <= _max.z));
}
//判斷點和包圍盒是否碰撞
bool AABB::containPoint(const Vec3& point) const
{
    if (point.x < _min.x) return false;
    if (point.y < _min.y) return false;
    if (point.z < _min.z) return false;
    if (point.x > _max.x) return false;
    if (point.y > _max.y) return false;
    if (point.z > _max.z) return false;
    return true;
}
//生成一個新的包圍盒 同時容納兩個包圍盒
void AABB::merge(const AABB& box)
{
    // 計算新的最小點座標
    _min.x = std::min(_min.x, box._min.x);
    _min.y = std::min(_min.y, box._min.y);
    _min.z = std::min(_min.z, box._min.z);
 
    // 計算新的最大點座標
    _max.x = std::max(_max.x, box._max.x);
    _max.y = std::max(_max.y, box._max.y);
    _max.z = std::max(_max.z, box._max.z);
}
//設置最大頂點與最小頂點
void AABB::set(const Vec3& min, const Vec3& max)
{
    this->_min = min;
    this->_max = max;
}
//頂點復位 初始化信息
void AABB::reset()
{
    _min.set(99999.0f, 99999.0f, 99999.0f);
    _max.set(-99999.0f, -99999.0f, -99999.0f);
}
//檢測座標信息是否有誤
bool AABB::isEmpty() const
{
    return _min.x > _max.x || _min.y > _max.y || _min.z > _max.z;
}
//由給定點座標點重新確定最大最小的座標向量
void AABB::updateMinMax(const Vec3* point, ssize_t num)
{
    for (ssize_t i = 0; i < num; i++)
    {
        // 最小x座標
        if (point[i].x < _min.x)
            _min.x = point[i].x;
         
        // 最小y座標
        if (point[i].y < _min.y)
            _min.y = point[i].y;
         
        // 最小z座標
        if (point[i].z < _min.z)
            _min.z = point[i].z;
         
        // 最大x座標
        if (point[i].x > _max.x)
            _max.x = point[i].x;
         
        // 最大y座標
        if (point[i].y > _max.y)
            _max.y = point[i].y;
         
        // 最大z座標
        if (point[i].z > _max.z)
            _max.z = point[i].z;
    }
}
//通過給定的變換矩陣對包圍盒進行變換
void AABB::transform(const Mat4& mat)
{
    Vec3 corners[8]; //保存包圍盒八個頂點
    //朝向z軸正方向的面
    //左上頂點座標
    corners[0].set(_min.x, _max.y, _max.z);
    //左下頂點座標
    corners[1].set(_min.x, _min.y, _max.z);
    //右下頂點座標
    corners[2].set(_max.x, _min.y, _max.z);
    //右上頂點座標
    corners[3].set(_max.x, _max.y, _max.z);
 
    //朝向z軸負方向的面
    //右上頂點座標
    corners[4].set(_max.x, _max.y, _min.z);
    //右下頂點座標
    corners[5].set(_max.x, _min.y, _min.z);
    //左下頂點座標
    corners[6].set(_min.x, _min.y, _min.z);
    //左上頂點座標
    corners[7].set(_min.x, _max.y, _min.z);
 
   
    //頂點變換
    for (int i = 0; i < 8; i++)
        mat.transformPoint(&corners[i]);
    //復位最大頂點最小頂點
    reset();
    //重新計算最大最小點信息
    updateMinMax(corners, 8);
}
 
NS_CC_END


4.總結

最後,AABB碰撞檢測算法雖然計算方法簡單,速度快,但是僅適用於精度不搞的遊戲中。相對於AABB碰撞檢測,還有一種更逼近物體並更爲精確的一種算法——OBB碰撞檢測。在Cocos2d-x中同樣提供了OBB碰撞檢測的相應方法,如圖4-1所示:

1411695033983458.jpg

圖4-1


來源網址:http://blog.csdn.net/u012945598/article/details/39524343

發佈了21 篇原創文章 · 獲贊 6 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章