2d重力模拟器-朝着大物体的中心轨道运行的小物体

tmb3ates  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(571)

在我的libgdx测试游戏中,我最初创建了两个圆形对象(恒星),下面我将详细介绍它们。然而,我的目标是在2d中模拟重力,较小的物体朝着较大物体的中心运行(就像地球绕太阳运行一样),但越来越靠近较大物体的中心。
因此,我创建了以下内容:
恒星1:更小,10px半径,10质量,动态(可以移动),初始位置-160x90,速度(0,0),加速器(0,0)
恒星2:更大,30像素半径,30质量,静止(不能移动),初始位置0x0,速度(0,0),加速器(0,0)
这里还有一个截图,只需要一张图片:

我将在下面给出我到目前为止所做工作的完整代码,在此之前我想提到我有两种方法,stellartest1和stellartest2。
首先,我试着给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,在本例中,您将看到相同的代码,除了我在这里使用Angular (单位:度):

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;
        }
    }

}
cx6n0qe3

cx6n0qe31#

您在stellartest1中的更新方法在概念上对我来说很好(我假设 1000f 这个系数是调整引力常数(大物体的质量)的方法。然而,如果你想要轨道有一些额外的衰减,你需要在加速度中加入一些虚拟的速度相关阻力项。没有必要使用stellartest2,因为当cos和sin的计算速度较慢且成本较高时,您应该会得到一个可比较的结果,而stellartest1中相同的分量是以纯代数的方式(乘法和除法)计算的,这要快得多。
但是要实现一些有趣的轨道,你不仅需要两个小物体初始位置的坐标,还需要两个小物体初始速度的坐标!如果不指定初始速度或者假设它为零,你就不会得到一个很好的曲线轨道。你需要选择初始速度。另外,轨道不应该离大物体的中心太远,因为牛顿引力场在大物体的中心有一个奇点,所以小物体离这个奇点越近,轨道看起来越糟糕(数值上的误差会不成比例地扩大),你会把较小的天体从较大天体的中心射出也就不足为奇了。
一般来说,有一种方法可以选择一个速度,使较小的物体在预定轨道参数的椭圆轨道上运动:半长轴的长度 a ,轨道偏心率 e ,Angular omega 半长轴和水平x轴之间的夹角 f (称为真异常)在从大到小物体的位置矢量和半长轴之间。

相关问题