2010-06-01 64 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