Orbital Mechanics in unity

So I’m trying to recreate some python code I found on the interwebs in unity. I apologize in advance for my awful code, I’m self taught and probably have alot of bad habits, but here we go!

This is the python code I’m trying to recreate (which runs properly in python I checked)

https://fiftyexamples.readthedocs.io/en/latest/gravity.html

now I have my code split up between the data layer, and classes to display it in unity.

here is what I have:

Class 1: Body

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class Body {

    public double mass;
    public double vx;
    public double vy;
    public double px;
    public double py;


    public Body(double oMass, double oVx = 0, double oVy = 0, double oPx = 0, double oPy = 0){
  
        mass = oMass;
        vx = oVx;
        vy = oVy;
        px = oPx;
        py = oPy;
    }

    public Force Attraction(Body other){
        if (other == this)
        {
            Debug.Log ("cant check gravity on yourslef!");
            return Force.Null();
        }

        double sx = this.px;
        double sy = this.py;
        double ox = other.px;
        double oy = other.py;

        double dx = (ox - sx);
        double dy = (oy - sy);

        double d = Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dy, 2));
        if (d == 0) {
            Debug.Log ("Collision!!");
            return Force.Null();  
        }

        double f = SSystem.G * this.mass * other.mass / Math.Pow(d, 2);


        double theta = Math.Atan2 (dy, dx);
        double fx = Math.Cos (theta) * f;
        double fy = Math.Sin (theta) * f;

        //Debug.Log ("fx test" + fx);
        //Debug.Log("fy test" + fy);

        Force force = new Force(fx, fy);
        return force;
    }

}

Class 2: SSystem

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class SSystem {

    public static double G = 0.0000000000667428;
    public static double AU = (149.6e6 * 1000) ;

    public Dictionary<string, double> times = new Dictionary<string, double>();  // multiplyer for run speed

    public int startBodies = 3;

    private int step;

    public string timestepKey = "days";
    public double timestep;

    public Body[] bodies = new Body[3];
    Force[] force = new Force[3];

    public SSystem()
    {
        Debug.Log (G);
        Debug.Log (AU);
        times.Add ("seconds", 1);
        times.Add ("minutes", 60);
        times.Add ("hours", 3600);
        times.Add ("days", 86400);
        times.Add ("months", 2592000);
        Body sun = new Body(1.98892 * Math.Pow(10,30));
        Body earth = new Body (5.9742 * Math.Pow (10, 24), 0f, 29.783 * 1000, -1 * AU, 0);
        Body venus = new Body (4.8685 * Math.Pow (10, 24), 0f, -35.02 * 1000, 0.723 * AU, 0);
        bodies[0] = sun;
        bodies [1] = earth;
        bodies [2] = venus;
        for (int i = 0; i < force.Length; i++) {
            force [i] = Force.Null ();
        }
    }



    public void Update(float deltaTime)
    {
        if (!times.ContainsKey(timestepKey))
            {
            Debug.Log ("timestep key" + timestepKey + "doesnt exist in the dictionary");
                return;
            }
        timestep = times[timestepKey] * deltaTime;
  
        step = 0;
        foreach (Body body1 in bodies)
        {
            foreach (Body body2 in bodies)
            {
                if (body1 == body2) {
                    continue;
                }
                Force thisForce = body1.Attraction (body2);

                //Debug.Log ("this force x " + thisForce.Fx);
                //Debug.Log ("this force y " + thisForce.Fy);

                force[step] = force[step].AddForce (thisForce);

                //Debug.Log ("this force x " + force[step].Fx);
                //Debug.Log ("this force y " + force[step].Fy);
            }
            body1.vx += force [step].Fx / body1.mass * timestep;
            body1.vy += force [step].Fy / body1.mass * timestep;

            //Debug.Log ("body vx " + body.vx);
            //Debug.Log ("body vy " + body.vy);

            body1.px += body1.vx * timestep;
            body1.py += body1.vy * timestep;

            step++;
        }
        /*
        step = 0;
        foreach (Body body in bodies)
        {

            body.vx += force [step].Fx / body.mass * timestep;
            body.vy += force [step].Fy / body.mass * timestep;

            //Debug.Log ("body vx " + body.vx);
            //Debug.Log ("body vy " + body.vy);

            body.px += body.vx * timestep;
            body.py += body.vy * timestep;

        }
*/
    }

}

Class 3: Force

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Force {
    public double Fx;
    public double Fy;

    public Force(double fx, double fy)
    {
        Fx = fx;
        Fy = fy;
    }


    public Force AddForce(Force other){
        Force newForce = new Force (this.Fx + other.Fx, this.Fy + other.Fy);
        return newForce;
    }


    public static Force Null()
    {
        Force force = new Force (0, 0);
        return force;
    }

}

thats it for the data layer, these next two are monobehaviours,

Class 1: Game (this one is placed on an empty object and linked to the prefab(which is just a sphere with the other class “GameBody” attached

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class Game : MonoBehaviour {

    public SSystem sSystem;
    public GameObject bodyPrefab;
    private int index;
    public List<GameObject> oBodies;
    public double Scale = 250 / SSystem.AU ;
    void Start () {
        sSystem = new SSystem ();
        index = 0;
        foreach (Body body in sSystem.bodies)
        {
            oBodies.Add (Instantiate (bodyPrefab, new Vector3 ((float)body.px, (float)body.py), Quaternion.identity, this.transform));
            oBodies [index].GetComponent<GameBody> ().initilize (index);
                index++;
        }
    }
  
    // Update is called once per frame
    void Update () {
        index = 0;
        sSystem.Update(Time.deltaTime);
        foreach (GameObject body in oBodies) {
            body.GetComponent<GameBody>().update ((float)sSystem.bodies [index].px * (float)Scale, (float)sSystem.bodies [index].py * (float)Scale);
            index++;
        }
    }
}

and the last bit for each game object

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameBody : MonoBehaviour {

    public Game game;
    public int Index;


    void start(){
        game = GetComponent<Game> ();
    }
      
    public void initilize(int index){
        Index = index;
    }

    public void update(float px, float py){
        this.transform.position = new Vector3 (px, py);
    }

    void Update () {
        //this.transform.position = new Vector3 (game.sSystem.bodies [Index].px / 1000, game.sSystem.bodies [Index].py / 1000);
    }
}

Thanks in advance for the help, I’ve written it all tonight so there is probably a really obvious problem I’m missing.

Edit1: I guess I never really explained the problem, the planets are supposed to oribit the sun (which they do when you run the python code in python) but in unity they fall to the sun for a moment then blast off quite quickly. So im not sure if one of the math functions is failing to transfer properly or what…

Capitalization is critical on all identifiers. Your start() function and update() function in GameBody are NOT being called as MonoBehaviour methods. The correct identifiers are Start() and Update() if you want Unity to call them for you.

If the objects are falling to the sun, could it be because you are not giving them the correct tangential initial orbital velocity? Whatever the gravitational attraction and desired orbital distance, there is a very precise orbital velocity that must be initially imparted or else you will either fall into the planet, or else escape from the planet, or have an irregular elliptical orbit. Wolfram Alpha can give you the formula to calculate that initial tangential velocity.

Remember also that simulating continuous orbital mechanics with a discrete computer is always going to have error that accumulates over time. The ways of minimizing that error involve adjusting your lateral force term in order to maintain a certain orbital distance, or perhaps a certain orbital speed.

Also, for each simulated step you should be calculating the force lateral to the secant line between two discrete sample points in your simulation, rather than at the actual sample point, which also helps to minimize error.

Thank you for the reply!

The math “should” be correct as it runs perfectly fine in the python implementation… also I got lazy with my update in GameBody, its supposed to be lowercase as its called from Game but I didn’t feel like coming up with a less confusing name for it. I will take a more in depth look into the math, though I was trying to avoid it xD