2D组碰撞检测

2D Group Collision Detection

本文关键字:碰撞检测 2D      更新时间:2023-10-16

我正试图在游戏中实现碰撞检测,但一开始看起来很容易的东西已经变成了一个怪物,如果没有帮助,我无法完全应对。

游戏在某个时候将是RTS,但现在它只是一堆单元,可以被告知移动到屏幕上的特定位置。但是,这些单位一直消失在一个单位中,所以我为每个单位定义了边界框,并在游戏循环中移动它们以将它们分离后,测试它们之间的碰撞。

这不起作用,因为我没有实现一种方法来知道我正在转换的特定单元不会取代其他单元(我已经反复学习过了)。

我试图维持已经占领的阵地(或地区),以便转移到没有部队的地方。但在某些情况下,这也不起作用,例如,当一个单元在屏幕的四个角中的任何一个移动时,它不能超出屏幕的边界,也不能占据与它碰撞的单元所占据的区域。

我仍然很确定我过于复杂了,用不同的方法可以很容易地完成。

每个单元都有自己的边界球体、位置、方向矢量和速度矢量。

class Unit {
    friend class Party;
    protected:
        float xPos, yPos;
        float destX, destY;
        float detectionRange;
        Vector *vel;
        Vector *dest;
        int dir;
        int offset;
        int width, height;
        Circle *circle;                 //Bounding Circle
        ...
    public:
        ...
};
//Collision Checking
void Party::checkCollisions() {
    bool noCol;
    float dx, dy;
    Circle *mCircle = NULL;
    Circle *sCircle = NULL;
    do {
        noCol = true;
        for(int i=0; i<numUnits; i++) {
            mCircle = units[i]->getCircle();
            for(int j=0; j<numUnits; j++) {
                if(j==i) {
                    continue;
                }
                sCircle = units[j]->getCircle();
                if(mCircle->isColliding(sCircle)) {
                    noCol = false;
                   mCircle->getShifts(sCircle, &dx, &dy);
                   units[i]->shift(dx, dy);
                   units[j]->shift(-dx, -dy);
                }
            }
        }
    } while(noCol == false);
}
//IsColliding. This is overriden for isColliding(Circle *circle), but here
//you see the actual algorithm.
bool Circle::isColliding(float X, float Y, int rad) {
    float mag = sqrt((X-x)*(X-x) + (Y-y)*(Y-y));
    if(mag <= (radius + rad)){
        return true;
    }
    return false;
}
//Get Shifts
void Circle::getShifts(Circle *c, float *dx, float *dy) {
    float x1 = x - c->x;
    float y1 = y - c->y;
    if(x1 > -1 && x1 < 1) {
        x1 = 1;
    }
    if(y1 > -1 && y1 < 1) {
        y1 = 1;
    }
    *dx = x1/fabs(x1);
    *dy = y1/fabs(y1);
}

这段视频显示了我到目前为止所得到的,但很明显,它出现了严重的故障,并且有不必要的单元移动。

我想要的是,当两个或两个以上的单位聚集在一起时,它们会自然聚集。但并非所有单位都会在任何时候组成一个群体。由于每个单元具有不同的检测范围,因此可以从羊群中分离出一个或多个单元。我想要这个,这样以后我可以选择和移动不同的单位组。

  1. 为每个单元存储一个该单元想要的位置
  2. 继续将单元移动到该位置
  3. 对于ALL单位(在此步骤中处理所有单位):发生碰撞时,将该单位推离碰撞,距离SMALL。即,如果您需要将单位移动(x:10.0;y:10.0)以避免碰撞,请将其移动(x:1.0;y:1.0)。当两个单元发生碰撞时,可以同时将它们推离碰撞点,但要保持较小的步长,即使不能立即解决碰撞
  4. 重复#3 N次(N应该在10..50左右),或者直到任何单元都没有发生碰撞

这是一种愚蠢的方法,它会自动处理您的所有问题。当您将多个对象放置在同一位置,并且希望将它们移动到不再碰撞的合理位置时,该算法也很有用。

或者你可以学习博伊德。这里提供Java演示。这将在不滥用碰撞检测的情况下处理单元碰撞/编队。


struct Vec2{
    float x;
    float y;
    Vec2(){
    }
    Vec2(float x_, float y_)
    :x(x_), y(y_){
    }
    Vec2(const Vec2& other)
    :x(other.x), y(other.y){
    }
    Vec2& operator*=(const float f){
        x *= f; y *= f;
        return *this;
    }
};
struct Circle{
    Vec2 pos; 
    float radius;
};
float dot(const Vec2& arg1, const Vec2& arg2){
    return arg1.x*arg2.x + arg1.y*arg2.y;
}
float length(const Vec2& arg){
   return sqrtf(dot(arg, arg));
}
Vec2 operator-(const Vec2& arg1, const Vec2& arg2){
   return Vec2(arg1.x - arg2.x, arg1.y - arg2.y);
}
Vec2 scale(const Vec2& arg, const float scale){
   return Vec2(arg.x*scale, arg.y*scale);
}
bool collide(const Circle& c1, const Circle& c2){
    Vec2 diff = c1.pos - c2.pos;
    float maxSquareDistance = c1.radius+c2.radius;
    maxSquareDistance *= maxSquareDistance;
    return (dot(diff, diff) < maxSquareDistance);
}
Vec getShiftVector(const Circle& c1, const Circle& c2){
    diff = c2.pos - c1.pos;
    maxSquareDistance = c1.radius + c2.radius;
    maxSquareDistance *= maxSquareDistance;
    float squareDistance = dot(diff, diff);
    if (squareDistance > maxSquareDistance)
       return Vec2(0, 0);
    float distance = sqrtf(squareDistance)
    if (distance){
        diff.x /= distance;
        diff.y /= distance;
    }
    else{
        diff = Vec2(1, 0);
    }
    float scaleFactor = c1.radius + c2.radius - distance;
    diff.x *= scaleFactor;
    diff.y *= scaleFactor;
    return diff;        
}

这听起来有点像你的碰撞代码关闭了。你的边界体积是多少?旋转框?

尝试使用"圆"作为单位边界体积。大多数游戏都使用圆形单位,因为它既便宜又准确。另外,circle->circle冲突代码非常便宜。

如果所有部队都被告知要转移到特定的位置,他们应该继续推进其他部队,不要在彼此内部移动。我可以想象一些更糟糕的情况发生在大团体中,比如单位在其他团体中突然出现,但稍后会处理。

只要确保一旦一个单元与一个对象发生碰撞,代码就会将该单元的位置"推"回来。或者,按其方向*速度将其推回。

为什么不给每个单元应用一个方向向量,并让它们相对地朝向该向量?例如,如果生成的矢量指向东北45度,并且距离编组的当前位置30米(当前位置应该是整个编组的位置平均值,或者通过某种方案,例如涉及最近单元的方案),则只需将每个单元相对于其自身位置移到该位置。现在,你应该有一个碰撞系统设置,这样你就可以处理一些事情,比如敌人部队通过你的团队冲锋,或者你的团队接触到一些静止的物体。现在,对于你的角碰撞问题,如果你想让触摸单元推动被触摸的单元,你应该添加一个额外的碰撞检查,以确保碰撞单元所接触的单元能够重新定位(即,他没有被四面包围)。否则,你可以让你的触摸单元简单地停在原地。

您可以将此作为参考:http://www.gamespp.com/algorithms/collisiondetection/