Objects acting under each other's gravity

I’m trying to create a planet simulation in Unity, so planets are attracted towards each other by gravity. So I’m having to write all the movement myself. Anyway, here is what I have. I’m trying to make them move towards each other based on their mass.

var planet : Transform;
var numberOfPlanets = 10;
var minimumSize = 1;
var maximumSize = 10;
var position = Vector3.zero;
var planets = new Array ();
var masses = new Array ();
var total = Vector3.zero;
var speed = Vector3.zero;


function Start () {
	for(i=0; i<numberOfPlanets; i++){
		position = Vector3(Random.Range(-15,16), Random.Range(-15,16), Random.Range(-15,16));
		
		var newPlanet = Instantiate (planet, position, Quaternion.identity);
		planets.Push(newPlanet);
		var scaleAmount = Random.Range (0,6);
		newPlanet.localScale += Vector3(scaleAmount, scaleAmount, scaleAmount);
		masses.Push(scaleAmount);
	}
	calculate();
}

function calculate (){
for (k=0;k<numberOfPlanets;k++){
var indiv = planets [k];
	for(j=0;j<numberOfPlanets;j++){
		var planetRef = planets[j];
		total += ((planetRef.position-indiv.position) * masses[j]);
		speed = total;
			indiv.transform.Translate (speed);
		}
	}
}

However, they just fly away (more than 1,000,000 units travelled in one frame). So what am I doing wrong?

You should use rigidbodies in the planets, and forces to simulate gravity (ironically, you should also set useGravity to false!) For each planet, you should calculate the gravity forces of all other planets and sum them together to get the resulting force, then apply it with AddForce in FixedUpdate - something like this (planet script):

var g = 6.674E−11; // universal gravity constant
function FixedUpdate(){
  if (planets *!= transform){ // calculate only other planets gravity!*
 *var resForce = Vector3.zero;*
 *for (var i = 0; i < planets.length; i++){*
 _var dir = planets*.position - transform.position; // get the force direction*_
 _*var dist2 = dir.sqrMagnitude; // get the squared distance*_
 _*// calculate the force intensity using Newton's law*_
 _var gForce = g * rigidbody.mass * planets*.rigidbody.mass / dist2;*_
 _resForce += gForce * dir.normalized; // accumulate in the resulting force variable_
 _*}*_
 _*}*_
 _*rigidbody.AddForce(resForce); // apply the resulting gravity force*_
_*}*_
_*
*_

NOTE: The gravity constant applies to planet scale distances and masses; maybe you should use a larger value to have a barely noticeable effect in the small distances and masses you will actually use in your planet system. Just to have an idea, earth mass is about 6E24 kg, moon mass is about 7E22, and the distance earth-moon is 3.8E8 m. If you used a distance equal to 38 units in your system, the earth mass should be set to 6E20, or 600000000000000000000! The moon mass, on the other hand, should be only 7E18, or 7000000000000000000. Obviously, these numbers are waaaay too big. You could set earth mass to 600, for instance, and moon mass to 7, which is 1E18 times smaller. Compensate such scale change is a complicated thing to calculate, thus I suggest you to just try a g constant in the range 0.06 to 6 and watch what happens.

moved from the duplicated question

Your calculation doesn't have much in common with gravity. Your speed / acceleration will get smaller when the objects come closer to each other... That doesn't make much sense. You have to divide the mass by the distance squared. You also need to reset your "total" variable for each planet. Also you calculate the acceleration, not the speed so you have to store the current speed of each planet and add the acceleration each frame. Also you should do such calculations in FixedUpdate to be frame rate independent. Ohh and don't forget to scale your acceleration properly. In the real world the gravitational constant is a very very small number (10^-11) but it depends on the mass and distance proportion.

function calculate ()
{
    for (k=0;k<numberOfPlanets;k++)
    {
        var indiv = planets [k];
        total = Vector3.zero;
        for(j=0;j<numberOfPlanets;j++)
        {
            var planetRef = planets[j];
            var direction = (planetRef.position-indiv.position);
            total += (direction.normalized * masses[j]) / direction.sqrMagnitude;
            speed[k] += total;
            indiv.transform.Translate (speed[k]);
        }
    }
}

Your custom approach could be done easier with Unity's rigidbody component. It has a velocity and a mass. Just turn off the gravity. Then use this script on each planet:

import System.Collections.Generic;

static var G = 0.01f; // adjust with try & error

static var planets = List.<Rigidbody>();
private var myRigidbody : Rigidbody;

//for testing. Set the z value to give the planet an initial speed along it's z-axis
public var initialForwardSpeed : Vector3; 

function Awake()
{
    myRigidbody = rigidbody;
    myRigidbody.velocity = transform.TransformDirection(initialForwardSpeed);
}

function OnEnable()
{
    planets.Add(rigidbody);
}

function OnDisable()
{
    planets.Remove(rigidbody);
}

function FixedUpdate()
{
    var pos = myRigidbody.position;
    var acc = Vector3.zero;
    for(var planet in planets)
    {
        if (planet == myRigidbody)
            continue;
        var direction = (planet.position - pos);
        acc += G * (direction.normalized * planet.mass) / direction.sqrMagnitude; 
    }
    myRigidbody.velocity+= acc * Time.fixedDeltaTime;   
}

edit

I tested my script and i had two typos: One Vector3 was written with a small "v" and a comment had only one slash ;)

I've also tested some values. Had 3 planets:

1. Mass: 1000; Pos: 0,0,0; InitialSpeed: 0,0,0;    Scale:3
2. Mass:    1; Pos: 4,0,0; InitialSpeed: 0,0,2;    Scale:1
3. Mass:    1; Pos: 0,4,0; InitialSpeed: 0,0,1.8;  Scale:1

All planets are just spheres. The two smaller spheres move on an elliptical orbit around the big one as expected.

I've found another very confusing fact:

The for-(each)-loop works great with the generic List, but if i use #pragma strict it complains that the planet variable (of type Object) doesn’t have a position / or mass. Sure, so i changed it to:

for(var planet : Rigidbody in planets)

but now it complains that he can't cast an Object to a Rigidbody. "planets" is a strongly typed collection but UnityScript seems to ignore the type in for-each-loops

It works if you do it that way:

for(var planet : Rigidbody in planets.ToArray())

But that's not the point of using Lists :D. Does one of the UnityScript user have a good explanation for this?

This is a very interesting and intuitive thread, I have implemented all that was written here to create a small solar system… I have big ideas on what I want specifically in my game but I need help on ways it can be done.

I am a total beginner when it comes to Scripting but am willing to learn it eventually through understanding and experiment in these early stages, I have lots of questions to ask here so I hope that you good people can help me out. I want to incorporate a spaceship into the game so here are my questions, I will start with the simplest;

  1. how do I create both first person and third person spaceship controls that don’t interfere with the pull of gravity but incorporate them into the velocity of the ship, and switch between the two (third and first person)modes in game.

  2. how do I create the camera smooth follow control to follow the ship?

I have tried using the packaged scripts but they do not look very realistic in the context I want to use them in and they use the rigid bodies gravity even if it is unchecked!

any help would be much appreciated, I have many more questions but I would like to solve these ones first so I can get an in game view representation of what’s going on and how I can improve the feel of the game.

Thanks in advance

Chris :),This is a very interesting and intuitive thread, I have implemented all that was written here to create a small solar system… I have big ideas on what I want specifically in my game but I need help on ways it can be done.

I am a total beginner when it comes to Scripting but am willing to learn it eventually through understanding and experiment in these early stages, I have lots of questions to ask here so I hope that you good people can help me out. I want to incorporate a spaceship into the game so here are my questions, I will start with the simplest;

  1. how do I create both first person and third person spaceship controls that don’t interfere with the pull of gravity but incorporate them into the velocity of the ship, and switch between the two (third and first person)modes in game.

  2. how do I create the camera smooth follow control to follow the ship?

I have tried using the packaged scripts but they do not look very realistic in the context I want to use them in and they use the rigid bodies gravity even if it is unchecked!

any help would be much appreciated, I have many more questions but I would like to solve these ones first so I can get an in game view representation of what’s going on and how I can improve the feel of the game.

Chris :slight_smile:

For any developers out there who would like to implement this really easily, try out:

World Physics System ~ http://nimbusgarden.com/worldphysicssystem

The World Physics System is a celestial body & point gravity scripting interface for Unity, intended as a replacement for the stock downward gravity. WPS can be used to simulate planetary orbits, body-body attraction, or “snowballing” effects. You may use it to create spherical worlds “out-of-the-box,” but World Physics System is implemented robustly and is lightweight, providing you complete freedom in expressing your point gravitation creativity, complex and repulsive forces, allowing you to use the system as a subcomponent for other effects, such as spells, powerups, or goal-based Artificial Intelligences for NPC’s. With the WPS, the sky is truly the limit!

Available from the Unity Asset Store, here.

More information, here.