2d 重力模拟器 - 较小的物体绕着较大物体的中心运行
2d gravity simulator - smaller object orbiting towards center of bigger object
在我的 libgdx 测试游戏中,我最初创建了 2 个圆形对象(恒星),我将在下面详细说明。然而,我的目标是在 2d 中模拟重力,其中较小的物体绕着较大物体的中心运行(就像地球绕太阳运行一样)但越来越靠近较大物体的中心。
因此,我创建了以下内容:
- stellar1:较小,10px 半径,10 质量,动态(可以移动),初始位置 -160x90,速度 (0,0),加速器 (0,0)
- stellar2:更大,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
因素是调整更大 body 的引力 constant/mass 的方法)。然而,如果你想要一些额外的轨道衰减,你需要为加速度添加一些虚构的 velocity-dependent 阻力项。不需要 StellarTest2,因为你应该得到一个可比较的结果,而 cos 和 sin 的计算速度较慢且昂贵,而 StellarTest1 中的相同组件是以纯代数方式(乘法和除法)计算的,速度要快得多。
但是要实现一些有趣的轨道,你不仅需要小object初始位置的两个坐标,还需要小object初始速度的两个坐标!如果不指定初始速度或假设它为零,您将不会获得漂亮的弯曲轨道。您需要选择初始速度。另外,轨道不能靠近大object的中心,因为牛顿引力force-field在大body的中心有一个奇点,所以越靠近小body 到达那个奇点,轨道看起来会更糟(并且数值误差会不成比例)并且你得到较小的 body 从较大的中心射出也就不足为奇了.
一般来说,有一种方法可以选择将较小的 body 发送到具有预定义轨道参数的椭圆轨道上的速度:semi-major 轴的长度 a
,轨道偏心率 e
、semi-major 轴与水平面 x-axis 之间的角度 omega
以及来自大到小 body 和 semi-major 轴。
在我的 libgdx 测试游戏中,我最初创建了 2 个圆形对象(恒星),我将在下面详细说明。然而,我的目标是在 2d 中模拟重力,其中较小的物体绕着较大物体的中心运行(就像地球绕太阳运行一样)但越来越靠近较大物体的中心。
因此,我创建了以下内容:
- stellar1:较小,10px 半径,10 质量,动态(可以移动),初始位置 -160x90,速度 (0,0),加速器 (0,0)
- stellar2:更大,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
因素是调整更大 body 的引力 constant/mass 的方法)。然而,如果你想要一些额外的轨道衰减,你需要为加速度添加一些虚构的 velocity-dependent 阻力项。不需要 StellarTest2,因为你应该得到一个可比较的结果,而 cos 和 sin 的计算速度较慢且昂贵,而 StellarTest1 中的相同组件是以纯代数方式(乘法和除法)计算的,速度要快得多。
但是要实现一些有趣的轨道,你不仅需要小object初始位置的两个坐标,还需要小object初始速度的两个坐标!如果不指定初始速度或假设它为零,您将不会获得漂亮的弯曲轨道。您需要选择初始速度。另外,轨道不能靠近大object的中心,因为牛顿引力force-field在大body的中心有一个奇点,所以越靠近小body 到达那个奇点,轨道看起来会更糟(并且数值误差会不成比例)并且你得到较小的 body 从较大的中心射出也就不足为奇了.
一般来说,有一种方法可以选择将较小的 body 发送到具有预定义轨道参数的椭圆轨道上的速度:semi-major 轴的长度 a
,轨道偏心率 e
、semi-major 轴与水平面 x-axis 之间的角度 omega
以及来自大到小 body 和 semi-major 轴。