[SOLVED] Newtonian gravity

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By DaddyMonster

Morning all, I’ve just started with godot and thought I’d make a procedural solar system to get to grips with it and I’ve somehow managed to make anti-gravity! My poor little procedural planets are pushing apart…

func gravity(delta, obj):
	distance = target_position - current_position
	direction = distance.normalized()
	acceleration = direction * GRAVITY * mass / (distance*distance)
	current_position += acceleration * delta
	current_position = get_child(1).move_and_slide(current_position)

My target / current positions are the Vector3 of the global origins. So, all I want to do is the classic Fg = Gm1m2/r^2 which expands to M1A = GM1*M2/r^2, M1 cancels giving A = G * M / r^2 which I multiply by the direction. What I don’t get is that minusing the direction doesn’t change anything…

This is my first project so I’m sure it’s just a schoolboy error.


Think I’m on the road to the answer, looks like I’ve mixed up my translation with my basis. Let me see if I can solve this.

DaddyMonster | 2020-07-23 10:22

Surely this isnt the issue, but i think you should multiply acceleration by squared delta to get position.
Edit: Oh, i see you are using current_position in move_and_slide. Move and slide should take a velocity as parameter, and returns the updated velocity. You are updating current_position, and then using it as a position to get the direction. I mean, you are using current_position sometimes as velocity and sometimes as position. Do you have a minimal project to reproduce this on my machine?

p7f | 2020-07-23 14:06

Yeah, saw that thanks, silly mistake - that’s what I mean’t by mixing up my translation with my basis, on current_position - here’s the latest version.

func gravity(delta, obj):
	distance = target_position - current_position
	direction = distance.normalized()
	acceleration = direction * GRAVITY * mass / (distance * distance)
	if acceleration.x != acceleration.x:
		acceleration.x = 0.0
	if acceleration.y != acceleration.y:
		acceleration.y = 0.0
	if acceleration.z != acceleration.z:
		acceleration.z = 0.0
	velocity += acceleration * delta
	velocity = get_child(1).move_and_slide(velocity)
	current_position += velocity 

It’s definitely distance and not acceleration that needs squaring. I’m just testing this now, suspect something is still wrong.

I had to hack a fix here, when the objects were aligned on an axis the distance would be zero, eg Vector3(0, 0, 100) and I was getting a div by zero error so I had to add the ugly if acceleration.z != acceleration.z: and use the fact it’s not addressing a memory location as a bool. If you know a more elegant solution I’d appreciate it.

DaddyMonster | 2020-07-23 15:09

Right, fixed the problem and I now have orbiting planets. :slight_smile:

If anyone is interested the above had two mistakes:

  1. I passed distance in as a Vector3. This caused problems every time the planet passed an axis, distance would fall to or near zero and acceleration would go off the charts. Instead I just used distance_to and passed in a float.
  2. I was updating current_position in the loop. Ordinarily that would be fine (says he who’s been coding Godot for a week!) but because gravity has 11 decimal points and the mass of the moon was 1 * 10^18 we get floating point rounding errors which accumulate so instead I picked up the values afresh on each iteration. If I need more performance in the future what I could do is pass it back and then update it every tenth iteration or whatever to correct any drift. Anyway, it’s fine for now.

Really interesting little project.

DaddyMonster | 2020-07-23 17:09

cool you get it working! Would you mind posting the answer and selecting it so others can see its solved?

p7f | 2020-07-23 17:14

No probs, here you go:

func gravity(delta, obj_1, obj_2):
	obj_1.velocity += (obj_2.global_transform.origin\
		- obj_1.global_transform.origin).normalized()\
		* GRAVITY * obj_2.mass\
		/ pow((obj_2.global_transform.origin.\
		distance_to(obj_1.global_transform.origin)), 2) * delta

To solve an n body problem you just need to loop through each object in the scene and pass in the current object you’re evaluating as obj_1 and then the second object as obj_2. Obviously, don’t forget to add an evaluation on the loop so that it doesn’t pass the same object as obj_1 and obj_2 - or you’ll have made a black hole! :slight_smile:

DaddyMonster | 2020-07-24 11:05