- Home /
Trouble with Newton-ish gravity system.
Hello!
I made a little scene to make a gravity system that allow to have planets as gravity centers and even a Solar System (i hope). After a little bit more than a week of thinking and trying multiple things i finally took the gravity formula and scripted it to make my objects attract each other according to their mass and distance from each other. (in other words: applying the basics of Newtonian laws)
It works pretty well, i'm quite satisfied and will continue to work on it to make it work more like "real world" and then i plan to make games with it. I made it in 2D because... Why not! I love 2D and want to use this gravity system in 2D (it basicly changes nothing anyway, the problem doesn't come from that).
My problem is that when you have two objects of significantly different masses, the script on the bigger one gives multiples errors because the force applied to it is so low the engine can't measure it (at least that's how i understood the error) and the console is flooded in errors.
Here are all the details about my project (it's really basic, i just made very simple tests).
Here you have the very light object named "body". When i launch the scene it is given a force of 50 upward on the screen so it starts spinning around the Black dot (which is a -very, very veeery symbolic- planet)
and here you have the planet with it's 1000 mass to make it stay in the middle of the scene.
It works well, the white little square spins around it in a stable orbit and the Planet has very tiny forces aplied to it that makes it move a little (it's transform has a value multiplied by powers of ten -sorry if it's not the right words, i'm not english and really don't know how to say that...- so the move is not even noticable). I think it's not perfectly exact in the eyes of sciences at all but i don't really care, it looks accurate enough to me and anyone (i think).
Here is the Error. In a few seconds i have 999+ of it as it seems to be sent almost every frame...
Rigidbody2D.AddForce(force) assign attempt for 'Planet' is not valid. Input force is { NaN, NaN }.
UnityEngine.Rigidbody2D:AddForce(Vector2)
gravityScript:FixedUpdate() (at Assets/Scripts/gravityScript.cs:37)
And finally here is my script (the only one in the project so far)
using UnityEngine;
using System;
public class gravityScript : MonoBehaviour
{
public GameObject[] Attraction;
public float vel;
public float acceleration;
// Use this for initialization
void Start ()
{
gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(0.0f,vel)); //This line just gives a force to an object if you want to. Setting the public float vel to 0 will make it start normally.
Attraction = GameObject.FindGameObjectsWithTag("Bodies");
}
// Update is called once per frame
void Update ()
{
}
void FixedUpdate()
{
foreach(GameObject body in Attraction)
{
float massA = gameObject.GetComponent<Rigidbody2D>().mass; // the game object mass
float massB = body.GetComponent<Rigidbody2D>().mass; //the "other" object mass
float dcarré= Mathf.Pow(Vector2.Distance(gameObject.transform.position, body.transform.position),2); // distance between the objects powered by two (again, sorry if i say it wrong)
float formule = acceleration * (massA* massB )/dcarré; //Newtonian gravity formula as i found it on a highschool website.
body.GetComponent<Rigidbody2D>().AddForce((gameObject.transform.position - body.transform.position ).normalized * formule); // direction to apply the force and then the formula
// I did it step by step to make it clearer for myself and anyone who would read it. I think it's easy to go through it like that ;)
}
}
}
So, finally, here is my question:
How could i just ignore that error? My project works as i want and this error doesn't seem to have very much impact on anything. If you think i should solve it i'd be grateful to know why it's important and how to do so but my point here is that i just want to get rid of it and go on! xD
Thanks a lot for reading and giving any help. Oh and by the way... How do you english speaking people say "puissances de 10"? :P
Answer by OncaLupe · Nov 12, 2015 at 08:57 PM
The errors you are getting are not due to the mass differences. They are coming because the array 'Attraction' has every object in it, including the one the script is on. The script is going through every object and trying to apply forces, so when it comes to itself the distance is zero and you get NaN (NaN = Not a Number, results from trying to divide by zero). This can be fixed quickly by just adding an if() statement inside the foreach:
foreach(GameObject body in Attraction)
{
if(body != gameObject)
{
//do stuff
}
}
This was interesting to me, so I decided to go through and adjust your script a little bit. The result is two things. First is minor, the foreach loop inside FixedUpdate no longer contains the object the script is running on. Second is much larger, it is no longer running GetComponent multiple times every frame. GetComponent is quite slow, so it should be avoided if possible by storing the references. The result is much faster operation, which will help when you start adding more objects to the scene.
using UnityEngine;
using System.Collections;
public class gravityScript : MonoBehaviour
{
public GameObject[] AttractionObjects;
Rigidbody2D[] AttractionBodies;
Rigidbody2D myRigidbody2D;
public float vel;
public float acceleration;
//I like to keep variable creation during run to a minimum
float myMass;
float massB;
float dcarré;
float formule;
void Start ()
{
//Store reference to this object's Rigidbody2D. GetComponent is slow, so should be avoided when needed more than one time.
myRigidbody2D = GetComponent<Rigidbody2D>();
myRigidbody2D.AddForce(new Vector2(0.0f,vel)); //This line just gives a force to an object if you want to. Setting the public float vel to 0 will make it start normally.
AttractionObjects = GameObject.FindGameObjectsWithTag("Bodies");
//Setup length of Rigidbody2D array, one less than number of objects as we don't need ourselves in it
AttractionBodies = new Rigidbody2D[AttractionObjects.Length - 1];
int count = 0;
//Cycle through each object in the list
foreach(GameObject body in AttractionObjects)
{
//If it's not this object
if(body != gameObject)
{
//Add its Rigidbody2D to the array and increase counter.
AttractionBodies[count] = body.GetComponent<Rigidbody2D>();
++count;
}
}
myMass = myRigidbody2D.mass; // the game object mass.
}
void FixedUpdate()
{
foreach(Rigidbody2D body in AttractionBodies)
{
massB = body.mass; //the "other" object mass
dcarré = Mathf.Pow(Vector2.Distance(transform.position, body.transform.position),2); // distance between the objects to the power of two
formule = acceleration * (myMass * massB ) / dcarré; //Newtonian gravity formula as i found it on a highschool website.
body.AddForce((transform.position - body.transform.position ).normalized * formule); // direction to apply the force and then the formula
}
}
}
As for the large planet moving a little, that is actually accurate. Earth wobbles a little during it's path around the sun because of the moon moving around it.
And as for how you say "puissances de 10" in english, it's "powers of 10". When saying phrases with that in math, they usually go like: "5 to the power of 10".
The if statement got that error off, thanks ;) and thanks for the little language tip!
I noticed some problems we may encounter later with that system. The masses on the rigidbodies are very limited if you want to do a totally realistic gravity setup.
The lowest you can give is 0.0001 and the highest is 1000000. I don't why it's like that and maybe it's not really bothering in any way (i didn't calculate if you could proportionally scale down real world numbers to get in Unity limits) but i thougt to myself that it would be quite a problem for later: Let's say you want to add many mnay little objects on that planet (like life forms). Let's say they make a big party on the same side of the planet and all of them begin to jump on the ground. If they're enough and keep jumping long enough there's a point where they would make the planet move with their collisions with the ground... That would be a lot of fun for a player i guess, but objectively it would be dumb as hell!
That leads me to think that the Newton gravity law on itself isn't enough to make this physic setup work completely, i'll have to dig a little further on other real world physic rules to apply to these objects!
Do you think it would be appropriate to create a forum post to share it with anyone in the Unity community who would like to learn/take part in this project? Or just to share new stuff i put in this project here? $$anonymous$$y guess is that someone already did something like that so yeah... maybe it would be quite useless to create that forum post.