因此,经过数小时的谷歌搜索和阅读后,我发现使用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和asteroids [i] .vertices显然是各个子弹或小行星的每个顶点的向量列表。
老实说,我不是在寻找代码修正,只是一副新鲜的眼睛。
答案 0 :(得分:2)
结果我对这个定理的数学理解非常好。相反,问题在于我没有在顶点向量中包含多边形的中心点。
感谢大家的时间。
答案 1 :(得分:0)
你添加了这个错误的vectOffset
部分 - 你的小行星和子弹的坐标系都是一样的,对吧? (必须是,如果边界框测试有效。)
你的小行星是正方形吗?如果是这样,那么边界框测试将始终是准确的,realCollision
和collision
应始终相同。如果没有,那么你没有正确构建asteroidEdges
- 你需要迭代顶点的数量,而不是4。
但严重的是,将此代码作为一个单独的方法并为其编写单元测试,这是我运行代码以查看正在发生的事情的唯一方法。
答案 2 :(得分: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));
看起来你正在创建一个小行星克隆,在这种情况下你会期望子弹被旋转,但是这个代码总是将子弹视为完全直立。这可能是你的问题吗?
答案 3 :(得分:0)
可能有助于找到问题的方法是让子弹成为一个重点。它可能会解释代码其他部分的问题。另外,如果你的观点发生了碰撞,但子弹没有,你会得到一些具体的东西。
换句话说,在解决方案出现之前简化您的问题。 ;)
答案 4 :(得分:0)
除了整个偏移的东西,这是错误的,算法的其余部分似乎好。您是否尝试过追踪它来发现问题?
顺便说一句,有几种风格怪癖使代码难以一目了然地阅读:CrissCross::Data::LList
而不是“好老”std::vector
?这是一种快速而又脏的自包含算法实现。我有点测试过,但不保证:
#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);
}