二维球之间的引力
本文关键字:之间 二维 | 更新日期: 2023-09-27 18:04:53
我有一个在二维空间中移动的多个圆的模拟,它们之间有弹性碰撞。
我想在粒子之间添加一个引力,这样粒子就会朝着其他粒子移动,这取决于质量,等等。我该怎么做呢?
我的碰撞管理功能是这样的:
void manageCollision(Particle particleA, Particle particleB)
{
float distanceX = particleA.Position.X - particleB.Position.X;
float distanceY = particleA.Position.Y - particleB.Position.Y;
double collisionAngle = Math.Atan2(distanceY, distanceX);
double pA_magnitude = Math.Sqrt(particleA.Velocity.X * particleA.Velocity.X + particleA.Velocity.Y * particleA.Velocity.Y);
double pB_magnitude = Math.Sqrt(particleB.Velocity.X * particleB.Velocity.X + particleB.Velocity.Y * particleB.Velocity.Y);
double pA_direction = Math.Atan2(particleA.Velocity.Y, particleA.Velocity.X);
double pB_direction = Math.Atan2(particleB.Velocity.Y, particleB.Velocity.X);
double pA_newVelocityX = pA_magnitude * Math.Cos(pA_direction - collisionAngle);
double pA_newVelocityY = pA_magnitude * Math.Sin(pA_direction - collisionAngle);
double pB_newVelocityX = pB_magnitude * Math.Cos(pB_direction - collisionAngle);
double pB_newVelocityY = pB_magnitude * Math.Sin(pB_direction - collisionAngle);
double pA_finalVelocityX = ((particleA.Mass - particleB.Mass) * pA_newVelocityX + (particleB.Mass + particleB.Mass) * pB_newVelocityX) / (particleA.Mass + particleB.Mass);
double pB_finalVelocityX = ((particleA.Mass + particleA.Mass) * pA_newVelocityX + (particleB.Mass - particleA.Mass) * pB_newVelocityX) / (particleA.Mass + particleB.Mass);
double pA_finalVelocityY = pA_newVelocityY;
double pB_finalVelocityY = pB_newVelocityY;
particleA.Velocity = new Vector2((float)(Math.Cos(collisionAngle) * pA_finalVelocityX + Math.Cos(collisionAngle + Math.PI / 2) * pA_finalVelocityY), (float)(Math.Sin(collisionAngle) * pA_finalVelocityX + Math.Sin(collisionAngle + Math.PI / 2) * pA_finalVelocityY));
particleB.Velocity = new Vector2((float)(Math.Cos(collisionAngle) * pB_finalVelocityX + Math.Cos(collisionAngle + Math.PI / 2) * pB_finalVelocityY), (float)(Math.Sin(collisionAngle) * pB_finalVelocityX + Math.Sin(collisionAngle + Math.PI / 2) * pB_finalVelocityY));
}
每个球或粒子生成的质量和半径是随机的。
函数在更新类型的方法中调用,如下所示:
Vector2 globalGravity = new Vector2(0f, gravityScale / 6000);
for (int i = 0; i < particles.Count(); i++)
{
particles[i].Update((float)updateTimer.Interval, globalGravity);
Vector2 position = particles[i].Position;
Vector2 velocity = particles[i].Velocity;
collisionWallCheck(ref position, ref velocity, particles[i].Radius);
particles[i].Position = position;
particles[i].Velocity = velocity;
Particle pA = particles[i];
for (int k = i + 1; k < particles.Count(); k++)
{
Particle pB = particles[k];
Vector2 delta = pA.Position - pB.Position;
float dist = delta.Length();
if (dist < particles[i].Radius + particles[k].Radius && !particles[i].Colliding && !particles[k].Colliding)
{
particles[i].Colliding = true;
particles[k].Colliding = true;
manageCollision(particles[i], particles[k]);
particles[i].initColorTable(); // Upon collision, change the color
particles[k].initColorTable();
totalCollisions++;
}
else
{
particles[i].Colliding = false;
particles[k].Colliding = false;
}
}
}
我在存储每个球的初始位置,速度和质量。
我显然需要做的,而不知道如何实现,是:
- 计算重力的大小和方向 知道了力,就可以计算出每个物体的加速度。知道了加速度,就可以计算出新的速度。
- 知道了速度就可以计算出新的位置
我对它的方程有点不确定,我想从两个球之间的引力开始。
根据Steven的建议,这是新的集成代码。
void updateTimer_Tick(object sender, EventArgs e)
{
const double G = 6.67398 * 0.00000000001;
for (int i = 0; i < particles.Count(); i++)
{
double sumX = 0;
double sumY = 0;
Particle pA = particles[i];
for (int k = i + 1; k < particles.Count(); k++)
{
Particle pB = particles[k];
Vector2 delta = pA.Position - pB.Position;
float dist = delta.Length();
if (dist < particles[i].Radius + particles[k].Radius && !particles[i].Colliding && !particles[k].Colliding)
{
particles[i].Colliding = true;
particles[k].Colliding = true;
manageCollision(particles[i], particles[k]);
particles[i].initColorTable();
particles[k].initColorTable();
totalCollisions++;
particles[i].Colliding = false;
particles[k].Colliding = false;
}
else
{
double distanceX = particles[i].Position.X - particles[k].Position.X;
double distanceY = particles[i].Position.Y - particles[k].Position.Y;
double r = Math.Sqrt(Math.Pow(distanceX, 2) + Math.Pow(distanceY, 2));
double force = G * particles[i].Mass * particles[k].Mass / (r * r);
double theta = Math.Tan(distanceY / distanceX);
sumX += force * Math.Cos(theta);
sumY += force * Math.Sin(theta);
particles[i].Colliding = false;
particles[k].Colliding = false;
}
}
double netForce = Math.Sqrt(Math.Pow(sumX, 2) + Math.Pow(sumY, 2));
double a = netForce / particles[i].Mass;
double aTheta = Math.Tan(sumY / sumX);
// Here we get accelerations for X and Y. You can probably figure out velocities from here.
double aX = a * Math.Cos(aTheta);
double aY = a * Math.Sin(aTheta);
Vector2 accel = new Vector2((float)aX, (float)aY);
particles[i].Update((float)updateTimer.Interval, accel);
//particles[i].Update((float)updateTimer.Interval, globalGravity);
Vector2 position = particles[i].Position;
Vector2 velocity = particles[i].Velocity;
collisionWallCheck(ref position, ref velocity, particles[i].Radius);
particles[i].Position = position;
particles[i].Velocity = velocity + accel;
}
Draw();
}
粒子的Update函数很简单,在它使用全局重力矢量(0,0)之前。
public void Update(float timeStep, Vector2 gravity)
{
velocity = velocity + timeStep * gravity;
position = position + timeStep * velocity;
}
我现在不确定如何处理0的情况
首先计算作用在每个物体上的重力。这由
给出F = Gm1m2/r*r
其中m1和m2为两个物体的质量,G为引力常数,r为两个物体之间的距离。
现在,r是一个向量,所以你可能想把它分成单独的分量——Fx和Fy。你可以这样做:
Fx = F * cos(theta)
Fy = F * sin(theta)
对于每个质量,计算作用在它和其他物体上的重力。把矢量加起来就得到了重力的合力。(注意-该链接可用于您的兴趣,但需要很长时间才能达到目的)。在这一点上,你将有一个合力在每个物体上,从中你可以计算加速度。下面是实现这一点的代码:
const double G = 6.67398 * 0.00000000001;
for (int i = 0; i < particles.Count(); i++)
{
double sumX = 0;
double sumY = 0;
for (int j = 0; j < particles.Count(); j++)
{
// Don't add attraction to self
if (i == j)
continue;
double distanceX = particles[i].Position.X - particles[j].Position.X;
double distanceY = particles[i].Position.Y - particles[j].Position.Y;
double r = Math.Sqrt(Math.Pow(distanceX, 2) + Math.Pow(distanceY, 2));
double force = G * particles[i].Mass * particles[j].Mass / (r * r);
double theta = Math.Tan(distanceY / distanceX);
sumX += force * Math.Cos(theta);
sumY += force * Math.Sin(theta);
}
double netForce = Math.Sqrt(Math.Pow(sumX, 2) + Math.Pow(sumY, 2));
double a = netForce / particles[i].Mass;
double aTheta = Math.Tan(sumY / sumX);
// Here we get accelerations for X and Y. You can probably figure out velocities from here.
double aX = a * Math.Cos(aTheta);
double aY = a * Math.Sin(aTheta);
}
指出这不会考虑像0值这样的东西——你必须清理这段代码以处理特殊情况,然后它才能正常运行而不崩溃。
在你计算完所有的力之前不要更新任何位置,否则你将被列表中后面的元素所淘汰。
另一件值得注意的事情是:这个算法是O(n^2),所以如果你有几个以上的身体,它将需要大量的计算。不幸的是,事情就是这样;如果你找到一种快速计算大量天体引力的方法,你可能应该打电话给美国宇航局。
根据你的坐标系,你可能会发现y向量是反向的。这是因为欧几里得几何认为正的y值是"向上的",而程序员倾向于用正的单位从屏幕顶部"向下"来测量y。这会破坏你的角度和其他东西。
知道所有球的位置和它们的质量,你就可以计算出任意两个物体之间所感受到的力的矢量。找出从球A到所有其他球的向量——A到球B, A到C, A到D,等等。然后,简单地把所有A的矢量加起来,得到作用在A上的力的最终矢量。重复B -> A, B -> C,以此类推,得到B的矢量。对所有人执行此操作,计算新的速度,并根据步骤之间的时间量调整位置。