By Ruben Droogh
Creating your own little universe to fly around in is a fun challenge for space nerds like me. In this blog, I will talk about the way we implemented gravitational physics in our game Orbinauts, what we learned from it, and what to watch out for.
This guide shows what we've learned while creating our game Orbinauts (playable at https://golb.itch.io/orbinauts). The initial inspiration for this coding adventure was this great video by Sebastian Lague: Coding Adventure: Solar System. I can recommend this YouTube creator as a great source of inspiration. This guide assumes a basic understanding of working with Unity in C#, and a willingness to play with your own values and ideas to create your unique solar system. This is a loose guide to help you along, not something that will get you a set-in-stone result.
In this guide, we will be making the following:
In 1-body gravitational physics, our spaceship is influenced by only one (planetary) body at a time, while in n-body physics, it is influenced by multiple bodies simultaneously. You might be surprised to learn that n-body gravity is easier to implement than 1-body, but we found that this is more confusing for players and a nightmare to visualize clearly in the UI.
To implement n-body gravity, all you need to do is apply Newton's law of universal gravitation, which is the following equation:
double F = (G * m1 * m2) / (r * r);
Where F is the force acted upon both objects, G the gravitational constant (more about that later), m1 and m2 the masses of both objects and r the distance between the objects. To apply this to your spaceship, simply loop through all gravity sources, calculate the force that the source exerts on the ship using the above function, add them together, and apply this sum to the spaceship. We, however, want to apply 1-body physics, as described earlier.
Our own little universe has its own little rules, and some of them do not match the real universe. One important one is the gravitational constant. This is basically the setting for how strong the gravity is in the universe. This is very much simplified, of course. If you want to read more about it, go check Wikipedia, you nerd. In the real universe, G equates to 6.6743 × 10^-11. We can make a constants class to access our own G, and we can play with it in real-time to get a value that feels right.
public static class AstroPhysics
{
public static float G;
}
Let's create a little solar system for our ship to fly around in. We're keeping it simple: a sun, and two planets orbiting it. We can simulate this in two ways:
The first option is more computationally expensive, but more realistic. It can also simulate some more interesting orbital phenomena, like two planets orbiting each other. The second option is cheaper, simpler, and we can make the orbital distances match the realistic speed by doing some maths. Let's do that one then.
First: let's make a script that defines a GameObject as a Gravity Source
. It needs, for now, only one parameter: the mass. This describes how "heavy" the object is. You can assign any value you want, as long as it's consistent relative to the other bodies and your spaceship.
public class GravitySource : MonoBehaviour
{
public float mass;
}
Let's make a sun: take a sprite of your choice and drag it into the scene. Give it the Gravity Source
script and set the mass. Then create some planets. Name them something fun and think of some cool lore around it (very important since you want your game to be cool). Place them into the scene at the distance from the sun you want them to orbit, and make them a child of the sun GameObject. The scene will be structured by having each "center body" as parent of a orbiting body. Now, add the Gravity Source
script and set the mass.
We want to make the planets orbit the star, and for that we need some maths. Take a look at this snippet:
Mathf.Sqrt((AstroPhysics.g * centreGravitySource.mass) / this.radius);
This formula represents the orbital velocity of an object moving around a central mass. It is derived from Newton's Law of Universal Gravitation. We will use this to find out how fast our planet will spin around the sun. We will use this in a new script: Orbiting Body
. Add it to the planet orbiting the star. Let's create the basic setup for this class.
public class MovingBody : MonoBehaviour
{
public GameObject centerBody;
private float radius;
private float rotationSpeed;
void Start()
{
// Get the centerBody which this body orbits around
this.centerBody = GetParent();
this.radius = Vector3.Distance(centre.transform.position, transform.position);
this.rotationSpeed = Mathf.Sqrt((AstroPhysics.g * centerGravitySource.mass) / this.radius);
}
void FixedUpdate()
{
this.MoveInACircle();
}
}
When the Moving Body gets created, we find out the correct rotation speed based on the distance to and mass of the center body. Now we need the code to actually move the GameObject in a circle around the center.
public void MoveInACircle()
{
angle += ((rotateSpeed / radius) * Time.deltaTime) % (2 * Mathf.PI);
var offset = new Vector2(Mathf.Sin(angle), Mathf.Cos(angle)) * radius;
transform.position = (Vector2)centre.transform.position + offset;
}
We calculate the angle where the body should be positioned based on time and speed, and use some trigonometry to then find the actual position of the body. This is a pretty efficient way to move the body around the center. Notice we placed this function in the FixedUpdate method to increase performance, as this function does not need to be framerate-dependent. Now we have a planet orbiting a sun! The nice thing is, you can use this code to build moons around your planets, and asteroids around those moons. All you need are realistic (relative) masses, sprites, and positions to put them in.
All that's left is a little spaceship to explore your newly-created system in. Place a GameObject with a sprite and a Rigid Body
. I'll leave the controls up to you; you might keep it at only thrust and rotation or you can go all-out with a physics-based reaction control systems. I encourage you to follow your heart. Place your galactic campervan near one of the planets.
In order to fly safe in a 1-body gravity system, we need to know which body we're being gravitationally influenced by. Everything is exerting gravitiational force, but we only need to worry about the one that is close or strong enough to overpower the force of the others. We need a Sphere of Influence (SOI) system. We'll make a few additions to the Gravity Source
script to support it.
public class GravitySource : MonoBehaviour
{
public float mass;
public float sphereOfInfluenceRadius;
void Start()
{
this.sphereOfInfluenceRadius = this.GetSOIRadius();
}
float GetSOIRadius()
{
float affectedBodyMass = 1;
float squareDistance = AstroPhysics.g * affectedBodyMass * mass / minimumGravityForce;
return Mathf.Sqrt(squareDistance);
}
}
In the GetSOIRadius()
method, we're essentially trying to calculate the distance where the gravitational force of the body equals a certain threshold (minimumGravityForce
). The formula used for this is once again derived from Newton's law of universal gravitation. affectedBodyMass
is a placeholder unit mass. We don't really care about this mass since the only objects influenced by this SOI will be negligable in weight compared to the mass of a planet. We can now use this radius to know which planet is currently attracting our spaceship. Create a script called Gravity Influenced
and add it to the ship.
Now, our earlier scene structure comes in handy. We want to find the planet whose SOI we're in, which is as low as possible in the hierarchy.
// TODO: get code for this
// Apply the gravitational force of this planet
// Become child of this planet
// That should do it
When we spawn in, we don't want to immediately plunge into the gravity well of the planet we spawn around. Let's calculate the speed the ship needs to go to be in a stable orbit.
// TODO
Hopefully this guide got you excited about building your own solar system. If you want to expand on it further, here are some next steps you could work on:
If you want to see what this code has brought us, try out our game, Orbinauts at https://golb.itch.io/orbinauts.