SDL程式設計入門(28)每畫素碰撞檢測
每畫素碰撞檢測
一旦你知道如何檢查兩個矩形之間的碰撞,你可以檢查任何兩個影像之間的碰撞,因為所有的影像都是由矩形構成的。
在電子遊戲中,所有的東西都可以用矩形來做,甚至這個點:
沒看到?我們把它放大:
還沒看到?那現在呢?:
影像由正方形的畫素組成,正方形的畫素為矩形。 要進行每個畫素的碰撞檢測,我們要做的就是讓每個物件都有一組碰撞框,並檢查一組碰撞框與另一組碰撞框的碰撞,如下所示:
//在螢幕上移動的點
class Dot
{
public:
//The dimensions of the dot
static const int DOT_WIDTH = 20;
static const int DOT_HEIGHT = 20;
//Maximum axis velocity of the dot
static const int DOT_VEL = 1;
//Initializes the variables
Dot( int x, int y );
//Takes key presses and adjusts the dot's velocity
void handleEvent( SDL_Event& e );
//Moves the dot and checks collision
void move( std::vector<SDL_Rect>& otherColliders );
//Shows the dot on the screen
void render();
//獲取碰撞框
std::vector<SDL_Rect>& getColliders();
private:
//The X and Y offsets of the dot
int mPosX, mPosY;
//The velocity of the dot
int mVelX, mVelY;
//點的碰撞框
std::vector<SDL_Rect> mColliders;
//移動碰撞框相對於點的偏移
void shiftColliders();
};
這是我們的點,現在有了每畫素碰撞檢測。它的速度減少到每幀1畫素,使碰撞更容易看到。move函式現在接受了一個碰撞框的向量,所以我們可以對照檢查兩組碰撞。由於我們將有兩個點碰撞,我們需要能夠得到碰撞器,所以我們有一個函式來處理。
我們沒有一個單一的碰撞框,而是有一個碰撞器的向量。我們也有一個內部函式來移動碰撞器以匹配點的位置。
//Starts up SDL and creates window
bool init();
//Loads media
bool loadMedia();
//Frees media and shuts down SDL
void close();
//Box set collision detector
bool checkCollision( std::vector<SDL_Rect>& a, std::vector<SDL_Rect>& b );
在這裡,我們有了新的碰撞檢測器,它可以相互檢查碰撞框的集合。
Dot::Dot( int x, int y )
{
//初始化偏移量
mPosX = x;
mPosY = y;
//建立必要的SDL_Rects
mColliders.resize( 11 );
//初始化速度
mVelX = 0;
mVelY = 0;
//初始化碰撞框的寬度和高度。
mColliders[ 0 ].w = 6;
mColliders[ 0 ].h = 1;
mColliders[ 1 ].w = 10;
mColliders[ 1 ].h = 1;
mColliders[ 2 ].w = 14;
mColliders[ 2 ].h = 1;
mColliders[ 3 ].w = 16;
mColliders[ 3 ].h = 2;
mColliders[ 4 ].w = 18;
mColliders[ 4 ].h = 2;
mColliders[ 5 ].w = 20;
mColliders[ 5 ].h = 6;
mColliders[ 6 ].w = 18;
mColliders[ 6 ].h = 2;
mColliders[ 7 ].w = 16;
mColliders[ 7 ].h = 2;
mColliders[ 8 ].w = 14;
mColliders[ 8 ].h = 1;
mColliders[ 9 ].w = 10;
mColliders[ 9 ].h = 1;
mColliders[ 10 ].w = 6;
mColliders[ 10 ].h = 1;
//初始化相對於位置的碰撞器
shiftColliders();
}
就像之前一樣,我們必須在建構函式中設定碰撞器的尺寸。這裡唯一不同的是,我們有多個碰撞框需要設定。
void Dot::move( std::vector<SDL_Rect>& otherColliders ){
//Move the dot left or right
mPosX += mVelX;
shiftColliders();
//If the dot collided or went too far to the left or right
if( ( mPosX < 0 ) || ( mPosX + DOT_WIDTH > SCREEN_WIDTH ) || checkCollision( mColliders, otherColliders ) )
{
//Move back
mPosX -= mVelX;
shiftColliders();
}
//Move the dot up or down
mPosY += mVelY;
shiftColliders();
//If the dot collided or went too far up or down
if( ( mPosY < 0 ) || ( mPosY + DOT_HEIGHT > SCREEN_HEIGHT ) || checkCollision( mColliders, otherColliders ) )
{
//Move back
mPosY -= mVelY;
shiftColliders();
}
}
這個功能和之前的差不多。每當我們移動點,我們就移動碰撞器。在我們移動點之後,我們檢查它是否離開了螢幕或撞到了什麼東西。如果是這樣,我們就把點移回來,並把它的碰撞器也一起移動。
void Dot::shiftColliders(){
//行偏移量
int r = 0;
//通過點的碰撞框
for( int set = 0; set < mColliders.size(); ++set )
{
//將碰撞框居中
mColliders[ set ].x = mPosX + ( DOT_WIDTH - mColliders[ set ].w ) / 2;
//在它的行偏移處設定碰撞框
mColliders[ set ].y = mPosY + r;
//將行的偏移量向下移動到碰撞框的高度。
r += mColliders[ set ].h;
}
}
std::vector<SDL_Rect>& Dot::getColliders(){
return mColliders;
}
不要太擔心shiftColliders的工作原理。它是mColliders[ 0 ].x = …,mColliders[ 1 ].x = …等的簡便方法,它適用於這種特定情況。對於自己的每一個畫素物件,你會有自己的放置函式。
而在shiftColliders之後,要有一個獲取colliders的訪問函式。
bool checkCollision( std::vector<SDL_Rect>& a, std::vector<SDL_Rect>& b ){
//矩形的邊框
int leftA, leftB;
int rightA, rightB;
int topA, topB;
int bottomA, bottomB;
//通過A框
for( int Abox = 0; Abox < a.size(); Abox++ )
{
//計算矩形A的邊長
leftA = a[ Abox ].x;
rightA = a[ Abox ].x + a[ Abox ].w;
topA = a[ Abox ].y;
bottomA = a[ Abox ].y + a[ Abox ].h;
//通過B框
for( int Bbox = 0; Bbox < b.size(); Bbox++ )
{
//計算矩形B的邊長
leftB = b[ Bbox ].x;
rightB = b[ Bbox ].x + b[ Bbox ].w;
topB = b[ Bbox ].y;
bottomB = b[ Bbox ].y + b[ Bbox ].h;
//如果A的任何邊都不在B的外面
if( ( ( bottomA <= topB ) || ( topA >= bottomB ) || ( rightA <= leftB ) || ( leftA >= rightB ) ) == false )
{
//檢測到碰撞
return true;
}
}
}
//如果兩組碰撞框都沒有接觸
return false;
}
在我們的碰撞檢測函式中,我們有一個for迴圈,計算物件a中每個碰撞框的頂部/底部/左側/右側。
然後我們計算物件b中每個碰撞框的上/下/左/右,然後檢查是否沒有分離軸。如果沒有分離軸,我們返回true。如果我們通過這兩個集合而沒有碰撞,我們返回false。
//Main loop flag
bool quit = false;
//Event handler
SDL_Event e;
//將在螢幕上移動的點
Dot dot( 0, 0 );
//將要碰撞的點
Dot otherDot( SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4 );
在進入主迴圈之前,我們先宣告我們的點和我們要碰撞的另一個點。
//While application is running
while( !quit )
{
//Handle events on queue
while( SDL_PollEvent( &e ) != 0 )
{
//User requests quit
if( e.type == SDL_QUIT )
{
quit = true;
}
//Handle input for the dot
dot.handleEvent( e );
}
//移動點並檢查碰撞
dot.move( otherDot.getColliders() );
//Clear screen
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderClear( gRenderer );
//Render dots
dot.render();
otherDot.render();
//Update screen
SDL_RenderPresent( gRenderer );
}
再次在主迴圈中,為點處理事件,對點進行碰撞檢查,然後最後我們渲染我們的物件。
我經常被問到的一個問題是,如何製作一個載入影像並自動生成每畫素碰撞檢測的碰撞框集的函式。答案很簡單。
別這樣做
在大多數遊戲中,你不希望100%的準確性。碰撞框越多,你的碰撞檢查就越多,速度也越慢。大多數遊戲追求的是足夠接近,比如在《街頭霸王》中:
結果雖然不是畫素完美,但已經很接近了。
另外,我們還可以在這裡做一個優化。我們可以為點設定一個邊界框,封裝所有其他的碰撞框,然後在進入每個畫素的碰撞框之前先檢查這一個。這樣做確實會多增加一次碰撞檢測,但由於兩個物體不碰撞的可能性更大,所以更可能為我們節省額外的碰撞檢測。在遊戲中,通常使用具有不同細節級別的樹結構來完成此操作,以便儘早使用,以防止在每個畫素級別進行不必要的檢查。和之前的教程一樣,樹形結構不在這些教程的範圍內。
在 這裡下載本教程的媒體和原始碼。
關注我的公眾號:程式設計之路從0到1
相關文章
- SDL程式設計入門(29)圓形碰撞檢測程式設計
- SDL程式設計入門(26)運動程式設計
- SDL程式設計入門(25)限制幀率程式設計
- SDL程式設計入門(23)高階定時器程式設計定時器
- 碰撞檢測
- javascript矩形碰撞檢測程式碼JavaScript
- javascript圓形區域碰撞檢測程式碼JavaScript
- 寫給程式設計師的機器學習入門 (十二) - 臉部關鍵點檢測程式設計師機器學習
- 入門程式碼程式設計程式設計
- Shell 程式設計入門程式設計
- shell程式設計入門程式設計
- 裝置畫素、獨立畫素和css畫素CSS
- WPF 2D 碰撞檢測
- 亞畫素數值極值檢測演算法總結演算法
- 程式設計和網路程式設計入門程式設計
- 一畫素的恩怨情仇:程式設計師與設計師之間的那些事程式設計師
- SDL3 入門(5):紋理渲染
- 【OpenCV】訪問Mat影象中每個畫素的值OpenCV
- 【程式設計測試題】素數對、不要二、求和程式設計
- 【程式設計素質】程式設計思想總結程式設計
- 遊戲程式設計入門指南遊戲程式設計
- Number 1 — 程式設計入門程式設計
- Python程式設計入門Python程式設計
- csh shell程式設計入門程式設計
- TCSHshell程式設計入門(轉)程式設計
- shell程式設計入門指南程式設計
- 程式設計入門——壘積木學程式設計程式設計
- 學習 PixiJS — 碰撞檢測JS
- Unix/Linux環境C程式設計入門教程(28) 日期時間那些事兒LinuxC程式程式設計
- 《Cracking the Coding Interview程式設計師面試金典》----畫素翻轉View程式設計師面試
- 28-目標檢測
- 讀取BMP影象每一畫素點RGB資料
- 華為車輛碰撞檢測專利公佈,可檢測車輛能否碰撞潛在障礙物
- JAVA NIO程式設計入門(二)Java程式設計
- Flink DataStream 程式設計入門AST程式設計
- 程式設計入門學什麼?程式設計
- JAVA NIO程式設計入門(一)Java程式設計
- JAVA NIO 程式設計入門(三)Java程式設計