I'm trying to do a Solar System simulator in python, with the Ursina engine, with physics. It works correctly until the earth (the only planet existing for the moment) gets in the same position on one or two axis than the sun. Then it just starts to shake and clipping and no-clipping of reality for no reason, following an straight line, usually the z axis.
Ursina's discord answer wasn't too helpful, since they lend me a code with that didn't had physics or elliptical orbits, which are the base of what I'm trying to do. Here's the code:
from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
from ursina.texture_importer import load_texture
import math
app = Ursina()
time_multiplicator = 1
G = 0.004
class Sun(Entity):
def __init__(self, position, color, scale, mass):
super().__init__(
parent = scene,
position = position,
origin = (0,0),
model = "sphere",
color = color,
collider = "mesh",
scale = scale
)
self.mass = mass
self.attraction_active = True
sun = Sun(position = (25,5,5), color = color.yellow, scale = 10, mass = 1500)
class Planet(Entity):
def __init__(self, position, color, scale, mass):
super().__init__(
parent = scene,
position = position,
origin = (0,0),
model = "sphere",
color = color,
scale = scale
)
self.mass = mass
self.attraction_active = True
self.initial_velocity = 0.4
def attraction(self):
self.gravitational_attraction = 1 + G * (self.mass * sun.mass)/(distance(sun,self)**2)
self.force_angle = 57.2958 * (math.atan((sun.y - self.y)/(sun.x - self.x))) + 1
self.y_component = self.gravitational_attraction * math.sin(self.force_angle) + 1
self.x_component = self.gravitational_attraction * math.cos(self.force_angle) + 1
print(f"gravitational_attraction ::: {self.gravitational_attraction}")
print(f"force_angle ::: {self.force_angle}")
print(f"y_component ::: {self.y_component}")
print(f"x_component ::: {self.x_component}")
def update(self):
self.attraction()
self.z -= self.initial_velocity * time.dt
self.y += self.y_component * time.dt
self.x += self.x_component * time.dt
blue = Planet(position = (0,0,0), color = color.blue, scale = 1, mass = 100)
print(distance(sun, blue))
EditorCamera()
def input(key):
if key == "q":
camera.look_at(blue)
app.run()
caveat: I just started tinkering with ursina
... pretty neat.
Your orig code had the earth getting sucked into the sun and the jitter was because it was near co-located. I printed the x,y coords and watched a bit.
You have a couple issues with the math and the physics.
You need to keep track of velocities, not just positions, unless I'm missing something with ursina. Recall that we can integrate to get velocities from force and position from velocity...
delta_v = F * dt
delta_pos = velocity * dt
Also, you need to use math.atan2
because it keeps track of both the x and y coordinate signage so that when things cross axes, you still get the correct sign of the angle.
It wasn't clear why you were adding "+1" to everything, so I removed it.
So after that, it is just a (non trivial) matter of putting an initial velocity on the earth so that the orbit is stable and doesn't get (a) sucked in, or (b) go flinging off into space due to lack of orbital capture. I tinkered with the velocities a bit and the below has a rotational orbit, but a weird one. I think you can tinker with the below and get to a working model.
from ursina import *
import sys
from ursina.prefabs.first_person_controller import FirstPersonController
from ursina.texture_importer import load_texture
import math
app = Ursina()
time_multiplicator = .1
G = 0.004
class Sun(Entity):
def __init__(self, position, color, scale, mass):
super().__init__(
parent = scene,
position = position,
origin = (0,0,0),
model = "sphere",
color = color,
collider = "mesh",
scale = scale
)
self.mass = mass
self.attraction_active = True
sun = Sun(position = (25,15,0), color = color.yellow, scale = 10, mass = 1500)
class Planet(Entity):
def __init__(self, position, color, scale, mass):
super().__init__(
parent = scene,
position = position,
origin = (0,0,0),
model = "sphere",
color = color,
scale = scale
)
self.mass = mass
self.attraction_active = True
self.velocity = [1, -3, 0] # vector vx, vy, vz
def attraction(self):
self.gravitational_attraction = G * (self.mass * sun.mass)/(distance(sun,self)**2)
self.force_angle = math.atan2( (-self.y + sun.y),(-self.x + sun.x))
self.y_component = self.gravitational_attraction * math.sin(self.force_angle)
self.x_component = self.gravitational_attraction * math.cos(self.force_angle)
print(f"gravitational_attraction ::: {self.gravitational_attraction}")
print(f"force_angle ::: {self.force_angle}")
print(f"y_component ::: {self.y_component}")
print(f"x_component ::: {self.x_component}")
print(f"x, y ::: {self.x}, {self.y}")
#if self.force_angle < 0: sys.exit(-1)
def update(self):
self.attraction()
# update the velocities, with update of F * dt
self.velocity[2] += 0 # no z velocity
self.velocity[1] += self.y_component * time.dt
self.velocity[0] += self.x_component * time.dt
# now update the positions with update = vel * dt
self.x += self.velocity[0]* time.dt
self.y += self.velocity[1]* time.dt
self.z += self.velocity[2]* time.dt
blue = Planet(position = (0,0,0), color = color.blue, scale = 1, mass = 100)
print(distance(sun, blue))
#EditorCamera()
# def input(key):
# if key == "q":
camera.position=(0,0,-200)