I’m a physicist and as a first major project in Godot I am trying to make a ship that flies around the solar system. To accomplish this I have multiple “Orbiter” Object in the Root scene as well as a ship. To show the path the ship is going to take, each physics step I calculate a number of ticks ahead using the same algorithm (rkn-4) as I use to move the ship:
func update_path(line: Line2D):
line.clear_points()
var pos: Vector2 = global_position
var vel: Vector2 = current_speed
line.add_point(line.to_local(pos))
for i in range(prediction_steps):
#Calculate position
var next = rkn(get_gravity_acceleration, i*prediction_step, prediction_step, pos, vel)
pos = next[0]
vel = next[1]
line.add_point(line.to_local(pos))
This works great! The line is shown and extremely stable:
However to display the path around the planet as an orbit, each point must be transformed into the local reference frame with the planet in the center. Mathematically this is quite easy, just take the difference of the point and the position of the target origin at time t and then add to it the current position of the target:
func update_path_transformed(line: Line2D, target: Orbiter):
line.clear_points()
var pos: Vector2 = global_position
var vel: Vector2 = current_speed
for i in range(0, prediction_steps):
#Calculate position
var next = rkn(get_gravity_acceleration, i*prediction_step, prediction_step, pos, vel)
pos = next[0]
vel = next[1]
var local_pos: Vector2 = line.to_local(pos - target.get_future_position(i*prediction_step) + target.global_position)
line.add_point(local_pos)
This unfortunately seems quite unstable. For an orbit around the earth specifically the moon seems to have a significant effect on the orbit, shrinking and stretching it, even though the position in the global frame seems stable. The transform seems to add an accumulating shift to the position. I do not fully understand how the to_local function works, I tried reparenting the line to the scene but this results in the line not being shown at all for some reason.
I would greatly appreciate some help on this topic.
Without transform:
With Transform
The first point on this line should be exactly at the position of the ship marked by the dot. I initially believed this to be due to floating point precision (the solar system is roughly to scale so this shift is very small when compared to the distances between planets) but recompiling for double precision has not fixed this problem. For completeness here are the process function:
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
look_at(get_global_mouse_position())
if Input.is_action_pressed("accelerate"):
var vect = get_global_mouse_position() - position
current_speed += vect.normalized()*acceleration*delta
#Process Player Movement
var next = rkn(get_gravity_acceleration, 0, delta, position, current_speed)
position = next[0]
current_speed = next[1]
#Modify Prediction line of player
#var line: Line2D = $Path
if GlobalSignals.show_trajectory:
path.show()
if(reference_target is Orbiter and GlobalSignals.transform_trajectory_to_reference):
update_path_transformed(path, reference_target)
else:
update_path(path)
else:
path.hide()
The Gravity-Acceleration Function:
func get_gravity_acceleration(time:float, position:Vector2, velocity:Vector2):
var planets = get_tree().get_nodes_in_group("planets")
var grav_accel = Vector2.ZERO
for planet in planets:
# Calculate position of planet at time t
var planet_position: Vector2
if time == 0:
planet_position = planet.global_position
else:
planet_position = planet.get_future_position(time)
#Calculate position
var vect = planet_position - position
var r = vect.length()
if r>10:
grav_accel += vect.normalized() * planet.mass * G / (r*r)
return grav_accel
And the Runge-Kutta-Nystroem algorithm:
func rkn(f: Callable,t: float, dt: float, y: Vector2, dy: Vector2) -> Array[Vector2]:
var K1 = f.call(t, y, dy)
var K2 = f.call(t + 2./3*dt, y + 2./3*dt*dy - dt**2./9*K1, dy+2./3*Vector2(dt, dt))
var K3 = f.call(t + 2./3*dt, y + 2./3*dt*dy + dt**2*(2./9*K1), dy+dt*(1./3*K1 + 1./3*K2))
var next_y = y + dt*dy + dt**2 * (K1/4.+K3/4.)
var next_dy = dy + dt * (K1/4. + 3./4 * K3)
return [next_y, next_dy] #returns next position and velocity
Any ideas what might cause this or how to fix it?



