2d gravity simulator - smaller object orbiting towards center of bigger object

Viewed 139

In my libgdx test game, I have initially created 2 circle objects (stellar) about which I will say more in details below. However, my goal is to simulate gravity in 2d, where smaller object orbits towards center of bigger object (just like earth orbits sun) but closer and closer to the center of bigger.

So, I have created the following:

  • stellar1: smaller, 10px radius, 10 mass, dynamic (can move), initial position -160x90, velocity (0,0), accelerator (0,0)
  • stellar2: bigger, 30px radius, 30 mass, static (cannot move), initial position 0x0, velocity (0,0), accelerator (0,0)

Here is also a screenshot, just to have a picture: enter image description here

I will give the full code below of what I did so far, before that I want to mention I have 2 approaches, StellarTest1 and StellarTest2.

First StellarTest1, I've tried to add some extra values like 1000f to both x and y just to see something in action like:

velocity.x += 1000f * acceleration.x * deltaTime;
velocity.y += 1000f * acceleration.x * deltaTime;

Resulting in - smaller object gets towards the center of the bigger object, but once it reaches the center of bigger, the smaller object gets evicted to the opposite side. Not to mention how 1000f is not the correct value in the size of this coordinate system, but I am concerned about following computation:

acceleration.x = gravityForce * (diffX / distance)
acceleration.y = gravityForce * (diffY / distance)

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

}

Second StellarTest2, in this example, you will see same code, except I am using here angle in degrees:

float angleInDegrees = MathUtils.atan2(diffY, diffX) * MathUtils.radiansToDegrees;
...
acceleration.x = gravityForce * MathUtils.cos(angleInDegrees * deltaTime);
acceleration.y = gravityForce * MathUtils.sin(angleInDegrees * deltaTime);

In this test, I did not have to add some extra velocity to move smaller object. Also I achieved that smaller object will make a solid curve, but will not get dragger in the center. Instead, after some time it will evict out. However, still I face the issue that smaller object keeps curving in and out and towards center. Yet, I am curious if cos and sin is necessary here, and perhaps StellarTest1 is right approach.

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

}
1 Answers

Your update approach in StellarTest1 looks conceptually fine to me (I assume this 1000f factor is way to adjust the gravitational constant/mass of the bigger body). However, of you want some extra decay of the orbit, you need to add some fictitious velocity-dependent drag term to the acceleration. There is no need of StellarTest2, because you should get a comparable results while the calculation of cos and sin is slower and expensive, while the same components in StellarTest1 are calculated in a purely algebraic way (multiplication and division) which are much faster.

But to achieve some interesting orbit you need not only the two coordinates of the initial position of the smaller object, but also the two coordinates of the initial velocity of the smaller object! Without specifying the initial velocity or assuming it is zero you are not going to get a nice curved orbit. You need to choose initial velocity. Also, the orbits should not get nowhere near the center of the big object, because the Newtonian gravitational force-field has a singularity at the center of the bigger body, so the closer the smaller body gets to that singularity, the worse the orbit will look (and the numerical errors will blow out of proportion) and it is not surprising you are getting the smaller body shot out of the center of the bigger one.

In general there is a way to choose a velocity that will send the smaller body on an elliptic orbit with predefined orbital parameters: the length of the semi-major axis a, orbital eccentricity e, the angle omega between the semi-major axis and the horizontal x-axis and the angle f (called true anomaly) between the position vector from the bigger to the smaller body and the the semi-major axis.

Related