- Home /
Advice on reducing rounding errors with transform
Hi,
I've recently started a project that involves orbiting planets suns, moons around planets etc. However, I've run into a problem (that I assume is rounding/calculation error) that causes orbiting bodies to very slowly increase the distance between itself and the body it is orbiting.
Below is the code that all orbiting bodies use. The call stack is started from a simple base case in a separate script that calls CalcOrbit() on anything registered to it once per Update().
public class Orbit : MonoBehaviour, IOrbit {
public static float artificialBias = 500f;
public GameObject influence;
public float orbitalSpeed, mass, rotationSpeed, distance; //rotationSpeed and distance are public for debugging (inspector) purposes
private float orbitalAngle;
private ArrayList objectsInInfluence;
private Vector3 deltaPosition;
void Awake(){
objectsInInfluence = new ArrayList();
}
// Use this for initialization
void Start () {
influence.GetComponent<IOrbit>().Register(gameObject);
}
// Update is called once per frame
void Update () {
}
public void Register(GameObject toRegister){
objectsInInfluence.Add(toRegister);
}
public void CalcOrbit(Vector3 influencePosition, float massOfInfluence, Vector3 influenceDeltaPosition){
//Orbit Planet
//-Speed
distance = Vector3.Distance(transform.position, influencePosition);
if(orbitalSpeed == 0) orbitalSpeed = Mathf.Sqrt(massOfInfluence/Vector3.Distance(transform.position, influencePosition)) * artificialBias;
//-Angle
if(transform.position.x > influencePosition.x){
orbitalAngle = 360 - Vector3.Angle(Vector3.forward, influencePosition - transform.position) - 90;
}else{
orbitalAngle = Vector3.Angle(Vector3.forward, influencePosition - transform.position) - 90;
}
if(orbitalAngle < 0) orbitalAngle += 360;
//-Resultant Vector
deltaPosition.x = Mathf.Sin (orbitalAngle * Mathf.Deg2Rad);
deltaPosition.z = Mathf.Cos (orbitalAngle * Mathf.Deg2Rad);
deltaPosition += influenceDeltaPosition;
//Perform Translation
transform.position += deltaPosition * Time.deltaTime;
//Rotate Planet
transform.Rotate (Vector3.up * rotationSpeed * Time.deltaTime);
//Do for Children
foreach(GameObject obj in objectsInInfluence){
obj.GetComponent<Orbit>().CalcOrbit(transform.position, mass, deltaPosition);
}
}
}
I have not made use of Transform.RotateAround() as it causes suborbitals (moons and lower) to behave erratically.
One option that has occurred to me is to create a parent transform for the orbital as a child of and at the orbited body and rotate that, however, I'd rather avoid that unless no other option makes itself apparent. eg
Sun
Graphics & Other Bits
Planet1 Centre of Orbit
Planet1
Graphics & Other Bits
Moon1 Centre of Orbit
Moon1
Graphics & Other Bits
...etc
Planet2 Centre of Orbit
...etc
So, ultimately, my question is how can I reduce rounding and calculation errors to prevent my orbitals drifting outward on their orbits?
Answer by dethtoll · Mar 16, 2015 at 08:26 AM
I wouldn't call this 'rounding errors'. The approach you're using has general stability problems.
You are effectively applying forces inside Update(). If you want to use a force-based approach to update your orbits, then you should apply them in FixedUpdate(). Further, you might as well use Unity's physics engine at that point.
But I wouldn't recommend it. Since you're dealing with nested transforms, the simulation will drift eventually.
If the planets have stable orbits, you're better off computing the local position of each planet (i.e. relative to its parent) directly. In other words, don't use velocities (values multipled by delta time and then added to position). Instead, compute the location of the elliptical (or circular in your case) orbit and then transform into world space.
You should really consider using the transform hierarchy you're trying to resist (nested transforms). If you're just dealing with stable orbits, then I would go as far as calling that the 'right' thing to do.
If you're resisting that approach because you find it confusing to deal with local transforms, then treat this as a learning exercise. In graphics and games, understanding transform hierarchies is critical.
I don't really see how moving this into FixedUpdate would have much of an impact, as the only thing you're changing is the update interval.
Similar to the above, I don't see how calculating in local and converting to world would have any difference (as a matter of fact, during testing my sun has been at origin and therefore world == local). I can't see how you would calculate the next local position without making use of velocities, further explanation would be appreciated.
The reason I'm resistant to using Unity's nesting is it because it struck me as an inappropriate use of the tool initially, however the more I think about it the more it makes sense. I have no problem with working in local space.
Your answer
Follow this Question
Related Questions
Rounding transform.position? With parents 0 Answers
Rounding not working? 2 Answers
Can't jump in diagonal 1 Answer
I need help ! = Tranform.position ? 1 Answer