SDL程式設計入門(29)圓形碰撞檢測
圓形碰撞檢測
除了矩形碰撞框,圓是最常見的碰撞器形式。在這裡,我們將檢查兩個圓以及一個圓和一個框之間的碰撞。
檢查兩個圓之間的碰撞很容易。 你要做的就是檢查每個圓心之間的距離是否小於其半徑的總和(半徑是半徑的倍數)。
對於框/圓碰撞,必須在碰撞框上找到最接近圓心的點。 如果該點小於圓的半徑,則發生碰撞。
//一個圓結構體
struct Circle
{
int x, y;
int r;
};
SDL有一個內建的矩形結構,但我們必須自己製作一個有位置和半徑的圓形結構。
//The dot that will move around on the screen
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( SDL_Rect& square, Circle& circle );
//Shows the dot on the screen
void render();
//Gets collision circle
Circle& getCollider();
private:
//The X and Y offsets of the dot
int mPosX, mPosY;
//The velocity of the dot
int mVelX, mVelY;
//Dot's collision circle
Circle mCollider;
//Moves the collision circle relative to the dot's offset
void shiftColliders();
};
這裡是以前碰撞檢測教程中的點類,又增加了一些附加功能。移動函式接收了一個圓形和一個矩形,以便在移動時對其進行碰撞檢測。現在,我們也有了一個圓形碰撞器,而不是一個矩形碰撞器。
//圓、圓碰撞檢測器
bool checkCollision( Circle& a, Circle& b );
//圓形、矩形框碰撞檢測器
bool checkCollision( Circle& a, SDL_Rect& b );
//計算兩點之間的距離平方
double distanceSquared( int x1, int y1, int x2, int y2 );
在本教程中,我們有碰撞檢測函式,用於圓、圓和圓、矩形的碰撞。我們還有一個計算兩點之間距離平方的函式。
使用距離平方而不是距離是一個優化,我們將在後面詳細介紹。
Dot::Dot( int x, int y )
{
//Initialize the offsets
mPosX = x;
mPosY = y;
//設定碰撞圓尺寸
mCollider.r = DOT_WIDTH / 2;
//Initialize the velocity
mVelX = 0;
mVelY = 0;
//Move collider relative to the circle
shiftColliders();
}
建構函式接收一個位置並初始化碰撞器和速度。
void Dot::move( SDL_Rect& square, Circle& circle ){
//Move the dot left or right
mPosX += mVelX;
shiftColliders();
//If the dot collided or went too far to the left or right
if( ( mPosX - mCollider.r < 0 ) || ( mPosX + mCollider.r > SCREEN_WIDTH ) || checkCollision( mCollider, square ) || checkCollision( mCollider, circle ) )
{
//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 - mCollider.r < 0 ) || ( mPosY + mCollider.r > SCREEN_HEIGHT ) || checkCollision( mCollider, square ) || checkCollision( mCollider, circle ) )
{
//Move back
mPosY -= mVelY;
shiftColliders();
}
}
就像之前的碰撞檢測教程一樣,我們沿著x軸移動,對著螢幕邊緣檢查碰撞,對著其他場景物件檢查。如果點撞到了什麼東西,我們就往後移動。一如既往,每當點移動時,它的碰撞器也會隨之移動。
然後,我們再對y軸做一次檢查。
void Dot::render(){
//Show the dot
gDotTexture.render( mPosX - mCollider.r, mPosY - mCollider.r );
}
渲染程式碼有點不同。SDL_Rects的位置在左上方,而我們的圓圈結構的位置在中心。這意味著我們需要通過從x和y位置中減去半徑來將渲染位置偏移到圓的左上方。
bool checkCollision( Circle& a, Circle& b ){
//計算總半徑的平方
int totalRadiusSquared = a.r + b.r;
totalRadiusSquared = totalRadiusSquared * totalRadiusSquared;
//如果圓心之間的距離小於它們的半徑之和
if( distanceSquared( a.x, a.y, b.x, b.y ) < ( totalRadiusSquared ) )
{
//圓圈已經相撞
return true;
}
//否則沒有
return false;
}
這是我們的圓對圓碰撞檢測器。它簡單地檢查中心之間的距離平方是否小於半徑平方之和。如果小於,則說明發生了碰撞。
為什麼我們要使用距離平方而不是普通的距離?因為計算距離涉及到一個平方根,而計算平方根是一個相對昂貴的操作。幸運的是如果x>y,那麼x2>y2,所以我們只要比較距離平方就可以省去一個平方根的操作。
bool checkCollision( Circle& a, SDL_Rect& b ){
//碰撞框上的最近點
int cX, cY;
//尋找最近的X偏移量
if( a.x < b.x )
{
cX = b.x;
}
else if( a.x > b.x + b.w )
{
cX = b.x + b.w;
}
else
{
cX = a.x;
}
//尋找最近的y偏移量
if( a.y < b.y )
{
cY = b.y;
}
else if( a.y > b.y + b.h )
{
cY = b.y + b.h;
}
else
{
cY = a.y;
}
//如果最近的點在圓內
if( distanceSquared( a.x, a.y, cX, cY ) < a.r * a.r )
{
//這個矩形框和圓圈相撞了
return true;
}
//如果圖形沒有碰撞
return false;
}
要檢查框和圓是否發生碰撞,我們需要在框上找到最近的點。
如果圓的中心在框的左側,則最近點的x位置在框的左側。
如果圓的中心在框的右側,則最近點的x位置在框的右側。
如果圓的中心在框內,則最近點的x位置與圓的x位置相同。
在這裡,我們找到最近的y位置,就像我們找到x位置一樣。如果盒子上最近的點和圓心之間的距離平方小於圓的半徑平方,那麼就會發生碰撞。
double distanceSquared( int x1, int y1, int x2, int y2 ){
int deltaX = x2 - x1;
int deltaY = y2 - y1;
return deltaX*deltaX + deltaY*deltaY;
}
這裡是距離平方函式。它只是一個沒有平方根的距離計算 ( squareRoot( x^2 + y^2 ) ) 。
//將在螢幕上移動的點
Dot dot( Dot::DOT_WIDTH / 2, Dot::DOT_HEIGHT / 2 );
Dot otherDot( SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4 );
//設定牆面
SDL_Rect wall;
wall.x = 300;
wall.y = 40;
wall.w = 40;
wall.h = 400;
在進入主迴圈之前,我們先定義場景物件。
//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 );
}
//Move the dot and check collision
dot.move( wall, otherDot.getCollider() );
//Clear screen
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderClear( gRenderer );
//Render wall
SDL_SetRenderDrawColor( gRenderer, 0x00, 0x00, 0x00, 0xFF );
SDL_RenderDrawRect( gRenderer, &wall );
//Render dots
dot.render();
otherDot.render();
//Update screen
SDL_RenderPresent( gRenderer );
}
最後在我們的主迴圈中,我們處理輸入,用碰撞檢測移動點,並將場景物件渲染到螢幕上。
在 這裡下載本教程的媒體和原始碼。
關注我的公眾號:程式設計之路從0到1
相關文章
- SDL程式設計入門(28)每畫素碰撞檢測程式設計
- SDL程式設計入門(26)運動程式設計
- SDL程式設計入門(25)限制幀率程式設計
- SDL程式設計入門(23)高階定時器程式設計定時器
- SDL3 入門(4):選擇圖形引擎
- 碰撞檢測
- SDL3 入門(3):三角形
- Matlab圖形使用者介面程式設計初級入門Matlab程式設計
- 入門程式碼程式設計程式設計
- 學習 PixiJS — 碰撞檢測JS
- Python程式設計入門Python程式設計
- Shell 程式設計入門程式設計
- 程式設計和網路程式設計入門程式設計
- WPF 2D 碰撞檢測
- Number 1 — 程式設計入門程式設計
- Flink DataStream 程式設計入門AST程式設計
- java Swing程式設計入門Java程式設計
- 寫給程式設計師的機器學習入門 (十二) - 臉部關鍵點檢測程式設計師機器學習
- SDL3 入門(5):紋理渲染
- python如何檢測pygame中的碰撞PythonGAM
- JAVA NIO程式設計入門(二)Java程式設計
- JAVA NIO 程式設計入門(三)Java程式設計
- JAVA NIO程式設計入門(一)Java程式設計
- 遊戲程式設計入門指南遊戲程式設計
- Python 非同步程式設計入門Python非同步程式設計
- Linux入門---(三)Shell程式設計Linux程式設計
- JavaScript 非同步程式設計入門JavaScript非同步程式設計
- 程式設計入門學什麼?程式設計
- Linux系統程式設計入門Linux程式設計
- 使用四叉樹優化碰撞檢測優化
- 程式設計正規化 —— 函數語言程式設計入門程式設計函數
- 直播系統程式碼,訊息傳送框設計成橢圓形狀
- 【Linux】Linux系統程式設計入門Linux程式設計
- 響應式程式設計入門(RxJava)程式設計RxJava
- 14.1 Socket 套接字程式設計入門程式設計
- Go語言程式設計快速入門Go程式設計
- Java入門之基礎程式設計Java程式設計
- 程式設計入門先學什麼?程式設計