圆形碰撞反弹不能正常工作

时间:2012-03-31 07:44:54

标签: c++

我正在用C ++编写一个小小的物理模拟,它基本上会在屏幕上移动圆圈,当两个圆圈碰撞时,它们应该以与台球相同的方式弹跳。当圆圈确实相互碰撞时,大多数时候它们实际上会无限减速/它们似乎彼此粘在一起并变得静止。有时只有一个球会在碰撞中反弹,而另一个球会保留它的轨迹。这只是一个简单的2D模拟。

所以这就是我对检测/弹跳逻辑的看法:

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

我无法弄清楚我的功能有什么问题。感谢您提前提供任何帮助!

修改: 每个变量都声明为float

功能:

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

5 个答案:

答案 0 :(得分:2)

  1. 只有当球相互移动时才会使球碰撞(反射运动矢量) 。如果他们彼此远离,请不要处理碰撞。打破这个规则,他们将被粘在一起。
  2. 处理碰撞时,立即更新两个球。不要一次更新一个球。
  3. 您的移动矢量调整不正确。球不会相互反射,因为它们可以以不同的速度移动。
  4. 正确的运动调整(假设球具有相同的质量)应该看起来像这样:

    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关于游戏物理学的论文。

答案 1 :(得分:1)

目前尚不清楚你是如何调用这个函数的,但我认为我看到了这个问题。

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

这将更新ballA的成员变量,并将其移回一帧。但它没有更新ballB的位置或轨迹。

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

答案 2 :(得分:1)

我没有详细查看过你的代码,但没有考虑到这种类型的碰撞只能在动量帧的中心工作。现在,我假设你的球具有相同的质量。你所做的是取两个动量的平均值(或速度,因为它们具有相同的质量)并从速度中减去该平均值。执行计算,然后添加平均值。 Here是我提出的与此有关的问题。

答案 3 :(得分:1)

我知道这个问题已经很老了,但是仍然很重要,特别是对学生而言。答案中未提及的某些内容使我想做出贡献。

解决类似问题时遇到的一件事是 overlap 。也就是说,如果运动球完全重叠,碰撞检测将连续触发,从而使粘附行为具有OP所指的作用。

这里尝试通过将球移动到前一帧来防止这种情况,但是如果移动足够使球陷入的深度大于单个帧可以解释的范围,或者如果移动速度仅为正确,以便前面的帧不会触发碰撞,但是后面的帧重叠得太多。

由于原始检查的中心距小于或等于半径的总和,因此检测将在碰撞和重叠时触发。 解决此问题的一种方法是将测试分为检查碰撞(仅等于)或重叠(小于仅)。对于碰撞,请照常进行。但是对于重叠条件,您可以物理地移动一个球或另一个球(或两个都移动一半)重叠量。这样可以将它们定位在正确的“碰撞”位置,从而实现反弹功能的正确行为。

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

errors

一个球可以很容易地将两个球都移动一半,但是我发现一次移动一个球可以为我的使用带来令人满意的结果,并且可以将参数保留为const。

我希望这对将来的学生有帮助! (我也是一个学生,并且对此很陌生,所以请以我那句谚语中的盐味作为我的建议)

答案 4 :(得分:0)

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