0
\$\begingroup\$

I am currently messing around with a game where you control a black hole and use its gravity to fling planets at targets. I have the gravity calculations working pretty well when the planets start out stationary but I want to take it a step further and have the planets start out in circular orbit around the black hole.

Ideally what I want to do is place a planet, provide it with an orbital period, and have it orbit my black hole object in uniform circular motion. To do this I am following these steps:

  1. Use the objects initial distance from the black hole (\$r\$) and the given orbital period (\$T\$) to calculate the standard gravitational parameter of the system (\$\mu\$) via the equation \$\mu = \frac{4\pi^2r^3}{T^2}\$
  2. Find the tangential velocity of the circular motion via the equation \$v = \sqrt{\frac{\mu}{r}}\$
  3. Assign the object an initial velocity with a magnitude of \$v\$ and a direction perpendicular to the vector between the black hole and the object
  4. Every time the Update() function is called, apply a force to the object in the direction of the black hole that represents its gravitational pull \$F=\frac{\mu m}{r^2}\$ where \$m\$ is the mass of the object.

All of these equations came from the Wikipedia page on circular orbit.

When I do all of this I get planets that do orbit the black hole, but their orbits are elliptical and rather unstable while I want them to be perfectly circular. Here is my GravitationalForce script, which I attach to the planet object and provide the black hole object as otherBody.

public class GravitationalForce : MonoBehaviour
{
    public GameObject otherBody;

    public float orbital_period = 2;

    float grav_param;

    Rigidbody2D rigidbody2d;
    Rigidbody2D rigidbody2d_other;
    // Start is called before the first frame update
    void Start()
    {
        // get rigid bodies of this object and the object it is attracted to
        rigidbody2d = GetComponent<Rigidbody2D>();
        rigidbody2d_other = otherBody.GetComponent<Rigidbody2D>();

        // calculate the grav_param for this object
        Vector2 position_this = rigidbody2d.position;
        Vector2 position_other = rigidbody2d_other.position;

        Vector2 dist_vector = position_this - position_other;
        float dist = dist_vector.magnitude;


        grav_param = (4*Mathf.Pow(Mathf.PI, 2)*Mathf.Pow(dist, 3))/(Mathf.Pow(orbital_period, 2));

        // calculate and assign the initial velocity
        Vector2 dist_vector_rotated = new Vector2(-dist_vector.y, dist_vector.x);
        dist_vector_rotated.Normalize();

        Vector2 velocity_vector = dist_vector_rotated * (Mathf.Sqrt(grav_param/dist));

        rigidbody2d.velocity = velocity_vector;
    }

    // Update is called once per frame
    void Update()
    {
        // calculate gravitational force 
        Vector2 position_this = rigidbody2d.position;
        Vector2 position_other = rigidbody2d_other.position;

        float dist = Vector2.Distance(position_this, position_other);

        float force_magnitude = (grav_param * rigidbody2d.mass)/(Mathf.Pow(dist, 2));

        float theta = Mathf.Atan2(position_other.y - position_this.y, position_other.x - position_this.x);

        Vector2 force_vector = new Vector2(force_magnitude*Mathf.Cos(theta), force_magnitude*Mathf.Sin(theta));

        rigidbody2d.AddForce(force_vector);
    }
}

Any ideas on why my orbits are unstable and elliptical instead of stable and circular? My only thought is that circular orbit is a very sensitive state and rounding errors in the physics engine or floating point number weirdness might be causing it to deviate from the circular path. If that is the problem, is there any easy fix to that and if not, what could the problem be?

Thanks!

\$\endgroup\$
  • \$\begingroup\$ Why not use force_vector = - (grav_param * rigidbody2d.mass)/(Mathf.Pow(dist, 3))*(position_this - position_other);? This avoids all the trigonometry and easily generalizes to 3D. \$\endgroup\$ – Lutz Lehmann Nov 19 '19 at 11:07
  • \$\begingroup\$ Also consider that Box2D does substepping - during which your gravitational vector will be off-center. You could get around that writing a custom Joint, or maybe using MouseJoint to pull Bodies together using proper substepping. \$\endgroup\$ – Jonas Beck Nov 19 '19 at 16:00
  • \$\begingroup\$ There's actually a premade script called PointEffector2D which I used a while back, which acts similarly but the orbits (if you can get them into orbit) don't mess up. Also, keep in mind that Update() and FixedUpdate() are separate things. If you're using Unity's physics calculations, you should generally use FixedUpdate(), and RigidBody2D uses Unity's physics calculations. \$\endgroup\$ – John Hamilton Nov 21 '19 at 9:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.