我正在用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;
}
答案 0 :(得分:2)
正确的运动调整(假设球具有相同的质量)应该看起来像这样:
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 ballA
和Ball 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不一定是碰撞点。