二维组碰撞检测

时间:2012-03-10 20:49:06

标签: c++ collision-detection

我正在尝试在我的游戏中实现碰撞检测,但最初看起来很容易变成一个怪物,如果没有一些帮助我无法解决。

游戏将在某个时刻成为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);
}

This video显示了我到目前为止所获得的内容,但很明显它非常闪烁并且有不必要的单位移动。

我想要的是,当两个或更多单位聚集在一起时,它们会自然而然地聚集。但是所有单位都不会随时组成一个鸡群。由于每个单元具有不同的检测范围,因此可以将一个或多个单元与植绒分开。我希望这样,以后我可以选择和移动不同的单位组。

3 个答案:

答案 0 :(得分:2)

  1. 对于每个单位商店,该单位想要的位置。
  2. 继续将设备移动到该位置。
  3. 对于 ALL 单位(在此步骤中处理所有单位):发生碰撞时,将本机远离碰撞推动 SMALL 距离。即如果您需要移动单位(x:10.0; y:10.0)以避免碰撞,请将其移动(x:1.0; y:1.0)。当两个单位发生碰撞时,你可以立即将它们从碰撞点推开,但是即使没有立即解决碰撞,也要保持一小步。
  4. 重复#3 N次(N应该在10..50左右)或直到任何单位都没有发生碰撞。
  5. 这是一种愚蠢的方法,可以自动处理所有问题。当您将多个对象放置在同一位置并且您希望将它们移动到不再发生碰撞的合理位置时,此算法也很有用。

    或者你可以学习boids。 Java演示可用here。这将处理单元碰撞/形成而不会滥用碰撞检测。


    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;        
    }
    

答案 1 :(得分:0)

为什么不将方向向量应用于每个单位并让它们相对地朝向该向量?例如,如果您生成的向量指向东北45度并且距离您组的当前位置30米(当前位置应该是整个组的位置平均值,或者是某个方案,例如与最近单位有关的方案)将每个单元相对于自己的位置移动到该位置。现在,您应该设置一个碰撞系统,以便让您处理某些事情,例如通过您的小组或您的小组与某些静止物体接触的敌方单位。现在,至于你的角落碰撞问题,你应该添加一个额外的碰撞检查,以确保碰撞单位正在接触的单位能够重新定位自己(即他没有四面环绕)如果你想拥有触摸单位推动触摸的单元。否则你可以让你的触摸单元停在原地。

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

答案 2 :(得分:0)

听起来好像你的碰撞代码已关闭。什么是你的边界卷,盒子?旋转的盒子?

尝试使用圆圈作为单位边界体积。大多数游戏都使用单位圆,因为它更便宜和准确。加上圆圈&gt;圆形碰撞代码相当便宜。

如果告知所有单位移动到特定位置,他们应该继续推进其他单位而不是互相移动。我可以想象一些与大群体一起发生的奇怪情况,比如突然出现在其他群体中的情况,但是稍后会对此进行处理。

只要确保一个单位与某个对象发生碰撞,代码就会“推”出该单位的位置。或者,按方向*速度向后推。