环形碰撞反弹无法正常工作

Circular collision rebound not working properly

本文关键字:常工作 工作 碰撞      更新时间:2023-10-16

我正在用C++编写一个小的物理模拟,基本上是在屏幕上移动圆圈,当两个圆圈碰撞时,它们应该像台球一样反弹。当这些圆确实相互碰撞时,大多数时候它们实际上会无限减速/它们看起来会粘在一起并变得静止。有时只有一个球会在碰撞中反弹,而另一个会保持其轨迹。这只是一个简单的二维模拟。

以下是我的检测/跳弹逻辑:

bool Ball::BallCollision(Ball &b2)
{
    if (sqrt(pow(b2.x - x, 2) + pow(b2.y - y, 2)) <= b2.radius + radius) // Test for collision
    {
        normal[0] = (x - (x + b2.x) / 2) / radius; // Finds normal vector from point of collision to radius
        normal[1] = (y - (y + b2.y) / 2) / radius;
        xvel = xvel - 2 * (xvel * normal[0]) * normal[0]; // Sets the velocity vector to the reflection vector
        yvel = yvel - 2 * (yvel * normal[1]) * normal[1];
        ////x = xprev; // These just move the circle back a 'frame' so the collision
        ////y = yprev; // detection doesn't detect collision more than once.
                   // Not sure if working?
     }
}

我搞不清我的功能出了什么问题。感谢您提前提供的帮助!

编辑:每个变量都声明为浮动

功能:

void Ball::Move()
{
    xprev = x;
    yprev = y;
    x += xvel;
    y += yvel;
}
void Ball::DrawCircle()
{
    glColor3ub(100, 230, 150);
    glBegin(GL_POLYGON);
    for (int i = 0; i < 10; i++)
    {
        angle = i * (2*3.1415/10);
        newx = x + r*cos(angle);
        newy = y + r*sin(angle);
        glVertex2f(newx, newy);
    }
    glEnd();
}

循环:

    run_prev.clear(); // A vector, cleared every loop, that holds the Ball objects that collided
    for (int i = 0; i < num_balls; i++)
    {
        b[i].Move();
    }
    for (int i = 0; i < num_balls; i++)
    {
        b[i].WallCollision(); // Just wall collision detecting, that is working just fine
    }
    //The loop that checks for collisions... Am I executing this properly?
    for (int i = 0; i < num_balls; i++)
    {
        for (int j = 0; j < num_balls; j++)
        {
            if (i == j) continue;
            if (b[i].BallCollision(b[j]) == true)
            {
                run_prev.push_back(b[i]);
            }
        }
    }
    for (int i = 0; i < num_balls; i++)
    {
        b[i].DrawCircle();
    }
    //xprev and yprev are the x and y values of the frame before for each circle
    for (int i = 0; i < run_prev.size(); i++)
    {
        run_prev[i].x = run_prev[i].xprev;
        run_prev[i].y = run_prev[i].yprev;
    }
  1. 使球碰撞(反映运动矢量)仅当它们朝向彼此移动时。如果它们正在相互远离,请不要处理碰撞。打破这个规则,他们就会粘在一起
  2. 处理碰撞时,同时更新两个球。不要一次更新一个球
  3. 移动矢量调整不正确。球不会相互反射,因为它们可以以不同的速度移动

正确的移动调整(假设球的质量相等)应该是这样的:

pos1 and pos2 = positions;
v1 and v2 are movement vector (speed);
n is collision normal == normalize(pos1 - pos2);
collisionSpeed = dot((v2-v1), n);
collisionSpeed *= elasticy; (0.0..1.0);
v1 = v1 - dot(v1, n);
v2 = v2 - dot(v2, n);
v1 -= scale(n, collisionSpeed * 0.5);
v2 += scale(n, collisionSpeed * 0.5);

要理解这个公式,请查看牛顿定律(特别是脉冲)。或者查看Chris Hecker关于游戏物理学的论文。

目前尚不清楚如何调用此函数,但我认为我看到了问题所在。

假设您有Ball ballABall ballB,它们在当前帧中发生冲突,然后运行ballA.BallCollision(ballB)

这将更新ballA的成员变量,并将其向后移动一帧。但它不会更新ballB的位置或轨迹。

即使您也调用converse(ballB.BallCollision(ballA)),它也不会检测到碰撞,因为当您调用ballA.BallCollision(ballB)时,它会将ballA向后移动一帧。

我还没有详细查看您的代码,但它没有考虑到这种类型的碰撞只能在动量框架的中心工作。现在,我假设你们的球质量相等。你要做的是取两个动量的平均值(或者速度,因为它们有相同的质量),然后从速度中减去这个平均值。进行计算,并将平均值加回来。这是我问的可能与此有关的问题。

我知道这个问题很老了,但它仍然很重要,尤其是对学生来说。一些答案中没有提到的东西让我想做出贡献。

我在解决类似问题时遇到的一件事是重叠。也就是说,如果移动的球重叠了任何量,碰撞检测将连续触发,从而给出所指的OP。

这里有一种通过将球移动到前一帧来防止这种情况的尝试,但如果移动足够大,以至于球嵌入的球超过了一帧,或者如果移动速度刚好合适,使得前一帧不会引发碰撞,但后一帧重叠太远,这种尝试偶尔会失败。

由于最初的检查是针对小于或等于半径之和的中心距离,因此检测会在碰撞和重叠时触发。解决此问题的一种方法是将测试分为碰撞(仅等于)或重叠(仅小于)检查。对于碰撞,请照常进行。但对于重叠条件,您可以将一个球或另一个球(或两者都移动一半)移动重叠量。这将它们定位在正确的"位置";"碰撞";位置,这允许反弹函数的正确行为。

一次只移动一个球的重叠函数可能看起来像这样(不是真正的代码):

    if (distanceBetweenBallCenters < sumOfRadii){
       currentPosition = oldPosition - (distanceBetweenBallCenters - sumOfRadii) * (unitVectorFromSecondBallToFirstBall);
    }

一个人可以很容易地将两个球移动一半,但我发现一次移动一个球会给我的使用带来令人满意的结果,并允许我将参数保持为常量。

我希望这对未来的学生有帮助!(我也是一名学生,对这一点很陌生,所以请谨慎对待我的建议)

您计算法线的方法是错误的。如果球的半径不相等,(x+b2.x)/2不一定是碰撞点。