【问题标题】:2D Group Collision Detection二维组碰撞检测
【发布时间】:2012-03-27 20:22:30
【问题描述】:

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

游戏将在某个时候成为 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 显示了我目前所得到的结果,但很明显它出现了极大的故障并且有不必要的单位移动。

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

【问题讨论】:

    标签: c++ collision-detection


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

    这是一种愚蠢的方法,可以自动处理您的所有问题。当您将许多对象放置在同一个位置并且您想将它们移到一些不再发生碰撞的合理位置时,此算法也很有用。

    或者你可以学习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;        
    }
    

    【讨论】:

    • 我会试试的,感谢您的回答和链接。有什么方法可以确定,一旦我确定一个特定的单元没有发生任何碰撞,我不会将另一个单元推到第一个单元上?例如,我正在遍历我的单元列表,同时考虑一个我检查碰撞的主要单元。在检查与第一次迭代的碰撞后,我移动了主机。我发现它也与序列中的第二个单元发生碰撞,我移动它使其再次位于我检查的第一个单元的位置。我应该部署什么方法来避免这种情况?
    • @Rikonator:第 3 步适用于所有单元。在此步骤中,您将所有单位从碰撞中推开( 距离),然后重复此步骤,直到没有单位与任何东西碰撞,或者直到您用完步骤。如果您用完了步骤,这意味着您正试图将大量单元塞入非常小的空间。这种方法有一个学名,但我忘记了。我认为它与 IK 求解器有关 - 增量求解器或其他东西。您应该记住的另一件事是,在某些情况下,使用此方法的单位可能会超过其最大速度(如果有的话)。
    • @Rikonator:“我应该部署什么方法来避免这种情况?”这种方法已经解决了这个问题。只需在第 3 步中处理所有单元,将它们全部从它们的直接碰撞中推 small 距离,即使这不能立即解决碰撞,然后重复整个过程 50 次或直到没有碰撞。当您的单位使用边界球进行碰撞检测时,效果非常好。使用盒子的结果可能会更糟。
    • 感谢您的帮助。我很确定你的步骤,在研究了一些关于 boids 的知识后,我可以让它工作。当我添加障碍物并升级单位以进行寻路工作时,这也将帮助我。
    • 很抱歉再次打扰您,但我对您的算法的应用似乎不起作用。有太多的碰撞和移位,即使我重复第 3 步,直到只有在没有碰撞时才为真的标志变为真。我可能应该更多地研究针对这个特定问题的植绒。不过,感谢您的帮助。
    【解决方案2】:

    听起来您的碰撞代码已关闭。你的边界体积,盒子是多少?旋转框?

    尝试使用 Circles 作为单位边界体积。大多数游戏都在单位上使用圆圈,因为它更便宜且更准确。加上circle->circle碰撞代码非常便宜。

    如果所有单位都被告知移动到特定位置,他们应该继续向其他单位推进,而不是相互移动。我可以想象大型团体中发生的一些奇怪的情况,例如单位突然出现在其他人身上,但稍后再处理。

    只要确保一旦一个单元与一个物体发生碰撞,代码就会“推”出该单元的位置。或者,将其按方向 * 速度向后推。

    【讨论】:

    • 我目前正在使用矩形框。一旦我能够让碰撞真正起作用,我将转向更好的碰撞检测方法。我的问题是知道如何移动碰撞单元。我目前正在遍历单位列表,我认为这就是问题所在。我需要更好的数据结构或更好的方法来检查哪些单元发生冲突。
    【解决方案3】:

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

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

    【讨论】:

    • 我已经有了每个单元的方向向量。我应该在我的问题中提到这一点,但是这些单位是独立的实体,只有当两个或更多被定向到同一点时,它们才应该像一个群体或一群一样聚集在一起。我将添加一些代码以使我的问题更清楚。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-26
    • 2014-01-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多