2010-06-01 60 views
5

所以,谷歌搜索和閱讀小時後碰撞檢測的幫助,我發現,檢測使用SAT碰撞的基本過程是:需要與實施使用分離軸定理

for each edge of poly A 
    project A and B onto the normal for this edge 
    if intervals do not overlap, return false 
end for 

for each edge of poly B 
    project A and B onto the normal for this edge 
    if intervals do not overlap, return false 
end for 

不過,由於很多我嘗試在代碼中實現這一點,我無法通過它來檢測碰撞。我的當前代碼如下:

for (unsigned int i = 0; i < asteroids.size(); i++) { 
    if (asteroids.valid(i)) { 
     asteroids[i]->Update(); 

     // Player-Asteroid collision detection 
     bool collision = true; 
     SDL_Rect asteroidBox = asteroids[i]->boundingBox; 

     // Bullet-Asteroid collision detection 
     for (unsigned int j = 0; j < player.bullets.size(); j++) { 
      if (player.bullets.valid(j)) { 
       Bullet b = player.bullets[j]; 

       collision = true; 
       if (b.x + (b.w/2.0f) < asteroidBox.x - (asteroidBox.w/2.0f)) collision = false; 
       if (b.x - (b.w/2.0f) > asteroidBox.x + (asteroidBox.w/2.0f)) collision = false; 
       if (b.y - (b.h/2.0f) > asteroidBox.y + (asteroidBox.h/2.0f)) collision = false; 
       if (b.y + (b.h/2.0f) < asteroidBox.y - (asteroidBox.h/2.0f)) collision = false; 

       if (collision) { 
        bool realCollision = false; 

        float min1, max1, min2, max2; 

        // Create a list of vertices for the bullet 
        CrissCross::Data::LList<Vector2D *> bullVerts; 
        bullVerts.insert(new Vector2D(b.x - b.w/2.0f, b.y + b.h/2.0f)); 
        bullVerts.insert(new Vector2D(b.x - b.w/2.0f, b.y - b.h/2.0f)); 
        bullVerts.insert(new Vector2D(b.x + b.w/2.0f, b.y - b.h/2.0f)); 
        bullVerts.insert(new Vector2D(b.x + b.w/2.0f, b.y + b.h/2.0f)); 
        // Create a list of vectors of the edges of the bullet and the asteroid 
        CrissCross::Data::LList<Vector2D *> bullEdges; 
        CrissCross::Data::LList<Vector2D *> asteroidEdges; 
        for (int k = 0; k < 4; k++) { 
         int n = (k == 3) ? 0 : k + 1; 
         bullEdges.insert(new Vector2D(bullVerts[k]->x - bullVerts[n]->x, 
               bullVerts[k]->y - bullVerts[n]->y)); 
         asteroidEdges.insert(new Vector2D(asteroids[i]->vertices[k]->x - asteroids[i]->vertices[n]->x, 
                asteroids[i]->vertices[k]->y - asteroids[i]->vertices[n]->y)); 
        } 

        Vector2D *vectOffset = new Vector2D(asteroids[i]->center.x - b.x, asteroids[i]->center.y - b.y); 

        for (unsigned int k = 0; k < asteroidEdges.size(); k++) { 
         Vector2D *axis = asteroidEdges[k]->getPerpendicular(); 
         axis->normalize(); 
         min1 = max1 = axis->dotProduct(asteroids[i]->vertices[0]); 
         for (unsigned int l = 1; l < asteroids[i]->vertices.size(); l++) { 
          float test = axis->dotProduct(asteroids[i]->vertices[l]); 
          min1 = (test < min1) ? test : min1; 
          max1 = (test > max1) ? test : max1; 
         } 
         min2 = max2 = axis->dotProduct(bullVerts[0]); 
         for (unsigned int l = 1; l < bullVerts.size(); l++) { 
          float test = axis->dotProduct(bullVerts[l]); 
          min2 = (test < min2) ? test : min2; 
          max2 = (test > max2) ? test : max2; 
         } 
         float offset = axis->dotProduct(vectOffset); 
         min1 += offset; 
         max1 += offset; 
         delete axis; axis = NULL; 
         float d0 = min1 - max2; 
         float d1 = min2 - max1; 
         if (d0 > 0 || d1 > 0) { 
          realCollision = false; 
          break; 
         } else { 
          realCollision = true; 
         } 
        } 

        if (realCollision) { 
         for (unsigned int k = 0; k < bullEdges.size(); k++) { 
          Vector2D *axis = bullEdges[k]->getPerpendicular(); 
          axis->normalize(); 
          min1 = max1 = axis->dotProduct(asteroids[i]->vertices[0]); 
          for (unsigned int l = 1; l < asteroids[i]->vertices.size(); l++) { 
           float test = axis->dotProduct(asteroids[i]->vertices[l]); 
           min1 = (test < min1) ? test : min1; 
           max1 = (test > max1) ? test : max1; 
          } 
          min2 = max2 = axis->dotProduct(bullVerts[0]); 
          for (unsigned int l = 1; l < bullVerts.size(); l++) { 
           float test = axis->dotProduct(bullVerts[l]); 
           min2 = (test < min2) ? test : min2; 
           max2 = (test > max2) ? test : max2; 
          } 
          float offset = axis->dotProduct(vectOffset); 
          min1 += offset; 
          max1 += offset; 
          delete axis; axis = NULL; 
          float d0 = min1 - max2; 
          float d1 = min2 - max1; 
          if (d0 > 0 || d1 > 0) { 
           realCollision = false; 
           break; 
          } else { 
           realCollision = true; 
          } 
         } 
        } 
        if (realCollision) { 
         player.bullets.remove(j); 

         int numAsteroids; 
         float newDegree; 
         srand (j + asteroidBox.x); 
         if (asteroids[i]->degree == 90.0f) { 
          if (rand() % 2 == 1) { 
           numAsteroids = 3; 
           newDegree = 30.0f; 
          } else { 
           numAsteroids = 2; 
           newDegree = 45.0f; 
          } 
          for (int k = 0; k < numAsteroids; k++) 
           asteroids.insert(new Asteroid(asteroidBox.x + (10 * k), asteroidBox.y + (10 * k), newDegree)); 
         } 
         delete asteroids[i]; 
         asteroids.remove(i); 
        } 
        while (bullVerts.size()) { 
         delete bullVerts[0]; 
         bullVerts.remove(0); 
        } 
        while (bullEdges.size()) { 
         delete bullEdges[0]; 
         bullEdges.remove(0); 
        } 
        while (asteroidEdges.size()) { 
         delete asteroidEdges[0]; 
         asteroidEdges.remove(0); 
        } 

        delete vectOffset; vectOffset = NULL; 
       } 
      } 
     } 
    } 
} 

bullEdges是子彈的邊緣向量的列表,asteroidEdges是類似的,並且bullVerts和小行星[I] .vertices是,很明顯,每個頂點的向量的列表爲各自的子彈或小行星。

老實說,我不是在尋找代碼修正,只是一個新的眼睛。

+0

究竟是什麼問題? realCollision總是出錯?您的包圍盒測試是否正常工作?我沒有看到任何明顯的東西,你應該將碰撞檢測分成單獨的方法,以便你可以對它進行單元測試。 – 2010-06-01 02:31:40

+0

邊界框碰撞的作品,但realCollision幾乎總是以假結束。 – Eddie 2010-06-01 02:34:22

+0

用最新的代碼更新,閱讀另一篇文章,並遵循它的要點。 – Eddie 2010-06-01 02:38:09

回答

2

原來我對定理的數學理解非常好。相反,問題在於我沒有在頂點向量中包含多邊形的中心點。

謝謝大家的時間。

0

你已經添加了這個vectOffset這是錯誤的部分 - 你的小行星和子彈的座標系都是相同的,對吧? (如果邊框測試正在工作,它必須是)。

你的小行星是否正方形?如果是這樣,那麼邊界框測試總是準確的,並且realCollisioncollision應該始終相同。如果沒有,那麼你不適當地建立asteroidEdges - 你需要遍歷頂點的數量,而不是4.

但是,嚴重的是,使這個代碼成爲一個單獨的方法,併爲它寫一個單元測試,這是唯一的方法我可以運行你的代碼來看看發生了什麼。

+0

小行星都是4個頂點,但不是正方形。 – Eddie 2010-06-01 03:30:09

0

bullVerts.insert(new Vector2D(b.x - b.w/2.0f, b.y + b.h/2.0f)); bullVerts.insert(new Vector2D(b.x - b.w/2.0f, b.y - b.h/2.0f)); bullVerts.insert(new Vector2D(b.x + b.w/2.0f, b.y - b.h/2.0f)); bullVerts.insert(new Vector2D(b.x + b.w/2.0f, b.y + b.h/2.0f));

它看起來像你創建一個小行星克隆,在這種情況下,你所期望的子彈旋轉,但是這個代碼始終把子彈,就好像它是完全直立。這可能是你的問題嗎?

+0

我沒有想到這一點,我會看看我能做些什麼。 – Eddie 2010-06-01 03:30:45

+0

劃痕,子彈永遠不會旋轉。 – Eddie 2010-06-01 03:33:10

+0

嗯,在這種情況下,我找不到你的代碼有什麼問題,除了我(像Keith)不明白vectOffset應該做什麼。你有沒有試過註釋掉'float offset = ...'這行以及之後的兩行? – 2010-06-01 04:41:27

0

可能有助於發現問題的東西是讓子彈成爲一個重點。它可能會照亮代碼中其他部分的問題。另外,如果你的觀點發生了碰撞,但子彈沒有,你會得到具體的東西來看待。

換句話說,在解決方案出現之前,簡化您的問題。 ;)

+0

使用點也沒有碰撞。 :( – Eddie 2010-06-01 19:37:31

0

除了整個抵消的東西,這是越野車,其餘的算法似乎好的。你是否試圖通過它來發現問題?

BTW,有幾種風格的怪癖,使代碼難以一目瞭然閱讀:而不是分配所有這些臨時Vector2Ds的堆棧

  • 爲什麼指針無處不在,?
  • 爲什麼CrissCross::Data::LList而不是「好老」std::vector
  • 當然Vector2D有一個重載的操作符?

這裏有一個快速和骯髒的自包含的算法實現。我已經對它進行了一些測試,但不作任何保證:

#include <vector> 
#include <limits> 

using namespace std; 

class Vector2D 
{ 
public: 
    Vector2D() : x(0), y(0) {} 
    Vector2D(double x, double y) : x(x), y(y) {} 

    Vector2D operator-(const Vector2D &other) const 
    { 
    return Vector2D(x - other.x, y - other.y); 
    } 

    double dot(const Vector2D &other) const 
    { 
    return x * other.x + y*other.y; 
    } 

    Vector2D perp() const 
    { 
    return Vector2D(-y, x); 
    } 

    double x,y; 
}; 

bool checkCollisionOneSided(vector<Vector2D> &object1, vector<Vector2D> &object2) 
{ 
    int nume = object1.size(); 
    for(int i=0; i<nume; i++) 
    { 
     Vector2D edge = object1[(i+1)%nume] - object1[i]; 
     Vector2D normal = edge.perp(); 

     double min1 = numeric_limits<double>::infinity(); 
     double min2 = min1; 
     double max1 = -numeric_limits<double>::infinity(); 
     double max2 = max1; 

     for(int j=0; j<object1.size(); j++) 
    { 
     double dot = normal.dot(object1[j]); 
     min1 = std::min(min1, dot); 
     max1 = std::max(max1, dot); 
    } 
     for(int j=0; j<object2.size(); j++) 
    { 
     double dot = normal.dot(object2[j]); 
     min2 = std::min(min2, dot); 
     max2 = std::max(max2, dot); 
    } 

     if(min2 > max1 || min1 > max2) 
    return false; 
    } 
    return true; 
} 

bool isColliding(vector<Vector2D> &object1, vector<Vector2D> &object2) 
{ 
    return checkCollisionOneSided(object1, object2) && checkCollisionOneSided(object2, object1); 
} 
+0

對象1是一個頂點或邊的向量嗎? – Eddie 2010-06-01 22:03:08

+0

頂點。從這個邊減去連續的頂點(Vector2D edge = object1 [(i + 1)%nume] - object1 [i];) – user168715 2010-06-01 22:05:51

+0

另外,在如果它不清楚,最後一個使用的方法是isColliding,通過傳遞兩個頂點列表(在你的情況下,每個頂點都有四個頂點)checkCollisionOneSided只是一個輔助方法 – user168715 2010-06-01 22:06:54