java - 2d 重力模拟器 - 较小的物体绕着较大物体的中心运行
问题描述
在我的 libgdx 测试游戏中,我最初创建了 2 个圆形对象(恒星),我将在下面详细说明。然而,我的目标是在 2d 中模拟重力,其中较小的物体围绕较大物体的中心运行(就像地球围绕太阳运行一样),但越来越接近较大物体的中心。
因此,我创建了以下内容:
- stellar1:更小,10px 半径,10 质量,动态(可以移动),初始位置 -160x90,速度 (0,0),加速器 (0,0)
- 恒星2:更大,30px半径,30质量,静态(不能移动),初始位置0x0,速度(0,0),加速器(0,0)
我将在下面给出我到目前为止所做的完整代码,在此之前我想提一下我有两种方法,StellarTest1 和 StellarTest2。
首先StellarTest1,我尝试在 x 和 y 中添加一些额外的值,例如 1000f ,只是为了查看一些实际情况,例如:
velocity.x += 1000f * acceleration.x * deltaTime;
velocity.y += 1000f * acceleration.x * deltaTime;
导致 - 较小的物体朝向较大物体的中心,但一旦它到达较大物体的中心,较小的物体就会被驱逐到对面。更不用说 1000f 不是这个坐标系大小的正确值,但我担心以下计算:
acceleration.x = gravityForce * (diffX / distance)
acceleration.y = gravityForce * (diffY / distance)
代码 StellarTest1:
public class StellarTest1 extends AbstractTest {
private Stellar stellar2, stellar1;
public StellarTest1(Game game) {
super(game);
}
@Override
public void create() {
game.getCartesianGrid().setEnabled(true);
// smaller stellar
float startX = -160;
float startY = 90;
float radius = 10;
float mass = 10;
stellar1 = new Stellar(
startX, startY,
radius, mass,
new Color(102, 188, 217, 100f)
);
// bigger stellar
startX = 0;
startY = 0;
radius = 30;
mass = 30;
stellar2 = new Stellar(
startX, startY,
radius, mass,
new Color(252, 236, 3, 100f)
);
stellar2.updatable = false; // bigger object will not update, in other words no motion
stellar2.setOther(stellar1);
stellar1.setOther(stellar2);
}
@Override
public void update(float deltaTime) {
if (!updatable) {
return;
}
stellar2.update(deltaTime);
stellar1.update(deltaTime);
}
@Override
public void draw() {
if (!drawable) {
return;
}
stellar2.draw();
stellar1.draw();
}
private class Stellar {
Circle circle;
Vector2 velocity;
Vector2 direction;
Vector2 acceleration;
float mass, radius;
boolean updatable;
Stellar other;
public Stellar(
float startX, float startY,
float radius, float mass,
Color color) {
this.radius = radius;
this.velocity = new Vector2(0, 0);
this.acceleration = new Vector2(0, 0);
this.mass = mass;
this.radius = radius;
circle = new Circle(game,
color,
startX, startY,
radius);
this.updatable = true;
}
public void update(float deltaTime) {
if (!updatable) {
return;
}
float diffX = other.circle.x - circle.x;
float diffY = other.circle.y - circle.y;
float G = 2f;
float mass = G * (other.mass - this.mass);
float distance = (float) Math.sqrt(Math.pow(diffX, 2) + Math.pow(diffY, 2));
float gravityForce = (float) (mass / Math.pow(distance, 2));
acceleration.x = gravityForce * (diffX / distance);
acceleration.y = gravityForce * (diffY / distance);
velocity.x += 1000f * acceleration.x * deltaTime;
velocity.y += 1000f * acceleration.y * deltaTime;
circle.x += velocity.x * deltaTime;
circle.y += velocity.y * deltaTime;
}
public void draw() {
game.getShapeRenderer().begin(ShapeRenderer.ShapeType.Filled);
circle.draw();
game.getShapeRenderer().end();
}
public void setOther(Stellar other) {
this.other = other;
}
}
}
第二个 StellarTest2,在此示例中,您将看到相同的代码,除了我在这里使用角度为度数:
float angleInDegrees = MathUtils.atan2(diffY, diffX) * MathUtils.radiansToDegrees;
...
acceleration.x = gravityForce * MathUtils.cos(angleInDegrees * deltaTime);
acceleration.y = gravityForce * MathUtils.sin(angleInDegrees * deltaTime);
在这个测试中,我不必增加一些额外的速度来移动较小的物体。我还实现了较小的物体会形成一条实线,但不会在中心出现拖拽器。相反,一段时间后它会被驱逐出去。但是,我仍然面临较小的物体不断向中心弯曲和向外弯曲的问题。然而,我很好奇这里是否需要 cos 和 sin,也许 StellarTest1 是正确的方法。
代码 StellarTest2:
public class Stellar2Test extends AbstractTest {
private Stellar stellar1, stellar2;
public Stellar2Test(Game game) {
super(game);
}
@Override
public void create() {
game.getCartesianGrid().setEnabled(true);
float startX = -160;
float startY = -90;
float radius = 10;
float mass = 30;
stellar2 = new Stellar(
startX, startY,
radius, mass,
new Color(102, 188, 217, 100f)
);
startX = 0;
startY = 0;
radius = 30;
mass = 30;
stellar1 = new Stellar(
startX, startY,
radius, mass,
new Color(252, 236, 3, 100f)
);
stellar1.updatable = false;
stellar1.setOther(stellar2);
stellar2.setOther(stellar1);
}
@Override
public void update(float deltaTime) {
if (!updatable) {
return;
}
stellar1.update(deltaTime);
stellar2.update(deltaTime);
}
@Override
public void draw() {
if (!drawable) {
return;
}
stellar1.draw();
stellar2.draw();
}
private class Stellar {
Circle circle;
Vector2 velocity;
Vector2 acceleration;
float mass, radius;
boolean updatable;
Stellar other;
public Stellar(
float startX, float startY,
float radius, float mass,
Color color) {
this.radius = radius;
this.velocity = new Vector2(0, 0);
this.acceleration = new Vector2(0, 0);
this.mass = mass;
this.radius = radius;
circle = new Circle(game,
color,
startX, startY,
radius);
this.updatable = true;
}
public void update(float deltaTime) {
if (!updatable) {
return;
}
float diffX = other.circle.x - circle.x;
float diffY = other.circle.y - circle.y;
float angleInDegrees = MathUtils.atan2(diffY, diffX) * MathUtils.radiansToDegrees;
float G = 2;
float mass = (G * (other.mass * this.mass));
float distance = (float) Math.sqrt(Math.pow(diffX, 2) + Math.pow(diffY, 2));
float gravityForce = mass / distance;
acceleration.x = gravityForce * MathUtils.cos(angleInDegrees * deltaTime);
acceleration.y = gravityForce * MathUtils.sin(angleInDegrees * deltaTime);
velocity.x += acceleration.x * deltaTime;
velocity.y += acceleration.y * deltaTime;
circle.x += velocity.x * deltaTime;
circle.y += velocity.y * deltaTime;
}
public void draw() {
game.getShapeRenderer().begin(ShapeRenderer.ShapeType.Filled);
circle.draw();
game.getShapeRenderer().end();
}
public void setOther(Stellar other) {
this.other = other;
}
}
}
解决方案
您在 StellarTest1 中的更新方法对我来说在概念上看起来不错(我认为这个1000f
因素是调整较大物体的引力常数/质量的方法)。然而,如果你想要一些额外的轨道衰减,你需要在加速度中添加一些与速度相关的虚构阻力项。不需要 StellarTest2,因为您应该获得可比较的结果,而 cos 和 sin 的计算速度较慢且昂贵,而 StellarTest1 中的相同组件以纯代数方式(乘法和除法)计算,速度要快得多。
但是要实现一些有趣的轨道,您不仅需要较小物体的初始位置的两个坐标,还需要较小物体的初始速度的两个坐标!如果不指定初始速度或假设它为零,您将不会得到一个漂亮的弯曲轨道。您需要选择初始速度。另外,轨道不应该离大物体的中心很近,因为牛顿引力场在大物体的中心有一个奇点,所以小物体越接近这个奇点,轨道就会越差。看(并且数字错误会变得不成比例)并且您将较小的身体从较大的身体的中心射出也就不足为奇了。
一般来说,有一种方法可以选择一个速度,该速度会将较小的物体发送到具有预定义轨道参数的椭圆轨道上:半长轴的长度a
、轨道偏心率、半长轴与水平 x- 之间的e
角度轴和从大到小物体的位置矢量与半长轴之间omega
的角度(称为真异常)。f
推荐阅读
- azure - 新对象:找不到类型 [Microsoft.Open.AzureAD.Model.DomainFederationSettings]:验证程序集
- php - Laravel/Passport - 重新迁移后密钥不可用
- python - Django sorl:没有足够的值来解包(预期 2,得到 1)
- reactjs - Redux 错误:操作必须是普通对象。使用自定义中间件进行异步操作
- python - ModuleNotFoundError:没有名为“load_data”的模块
- android - 谷歌表格不适用于发布 apk 颤振
- php - 如何使 userEmail 外键连接到 Tbluser 模型中的 emailId
- c# - 在 C# 中拆分大字符串并将其中的值添加到列表中
- python - 没有在python的新文件中写入所有输出行
- c++ - 将子类转换为父类 C++