首页 > 解决方案 > 2d 重力模拟器 - 较小的物体绕着较大物体的中心运行

问题描述

在我的 libgdx 测试游戏中,我最初创建了 2 个圆形对象(恒星),我将在下面详细说明。然而,我的目标是在 2d 中模拟重力,其中较小的物体围绕较大物体的中心运行(就像地球围绕太阳运行一样),但越来越接近较大物体的中心。

因此,我创建了以下内容:

这里也是一个截图,只是为了有一个图片:在此处输入图像描述

我将在下面给出我到目前为止所做的完整代码,在此之前我想提一下我有两种方法,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;
        }
    }

}

标签: javalibgdxphysics

解决方案


您在 StellarTest1 中的更新方法对我来说在概念上看起来不错(我认为这个1000f因素是调整较大物体的引力常数/质量的方法)。然而,如果你想要一些额外的轨道衰减,你需要在加速度中添加一些与速度相关的虚构阻力项。不需要 StellarTest2,因为您应该获得可比较的结果,而 cos 和 sin 的计算速度较慢且昂贵,而 StellarTest1 中的相同组件以纯代数方式(乘法和除法)计算,速度要快得多。

但是要实现一些有趣的轨道,您不仅需要较小物体的初始位置的两个坐标,还需要较小物体的初始速度的两个坐标!如果不指定初始速度或假设它为零,您将不会得到一个漂亮的弯曲轨道。您需要选择初始速度。另外,轨道不应该离大物体的中心很近,因为牛顿引力场在大物体的中心有一个奇点,所以小物体越接近这个奇点,轨道就会越差。看(并且数字错误会变得不成比例)并且您将较小的身体从较大的身体的中心射出也就不足为奇了。

一般来说,有一种方法可以选择一个速度,该速度会将较小的物体发送到具有预定义轨道参数的椭圆轨道上:半长轴的长度a、轨道偏心率、半长轴与水平 x- 之间的e角度轴和从大到小物体的位置矢量与半长轴之间omega的角度(称为真异常)。f


推荐阅读