Problem with simple planet orbitting script

i used a planet orbitting script from a devfoum resources and editting to have eccentricty and orbital plane but there is a problem with my script which i dont know how to fix

The module script

local RunService = game:GetService("RunService")

local CosineMath = math.cos
local SineMath = math.sin
local Atan2Math = math.atan2

local PIMath = math.pi
local TAU = 2*PIMath
local TweenS = game:GetService('TweenService')
return function(PlanetOrbiting : PVInstance, PlanetGettingOrbited : PVInstance,Distance: number, TimeToOrbit : number, Eccentricity : number, OrbitalPlane : number)
	assert(PlanetOrbiting ~= PlanetGettingOrbited, "Cannot orbit itself, PlanetOrbiting and PlanetGettingOrbited should be different")

	local DifferenceVector = PlanetOrbiting:GetPivot().Position-PlanetGettingOrbited:GetPivot().Position --Differnce

	local Angle = Atan2Math(DifferenceVector.Y, DifferenceVector.X)--Angle
	local HeartbeatConnection
	HeartbeatConnection = RunService.Heartbeat:Connect(function(DeltaTime)
		-- Disconnect the event if one of the planets got destroyed
		if not (PlanetOrbiting or PlanetGettingOrbited) then
			HeartbeatConnection:Disconnect()
			HeartbeatConnection = nil
		end

		-- Polar coordinates 2D
		local x
		local y
		local z = 0
		local offset
		if Eccentricity < 0 then
			x = (Distance+-Eccentricity)*CosineMath(Angle)
			y = Distance*SineMath(Angle)
			offset = CFrame.new(Eccentricity/2,0,0)*CFrame.Angles(math.rad(OrbitalPlane)+math.rad(90),0,0)
		elseif Eccentricity == 0 then
			x = (Distance)*CosineMath(Angle)
			y = Distance*SineMath(Angle)
			offset = CFrame.new(Eccentricity,0,0)*CFrame.Angles(math.rad(OrbitalPlane)+math.rad(90),0,0)
		elseif Eccentricity > 0 then
			x = (Distance+Eccentricity)*CosineMath(Angle)
			y = Distance*SineMath(Angle)
			offset = CFrame.new(Eccentricity/2,0,0)*CFrame.Angles(math.rad(OrbitalPlane)+math.rad(90),0,0)
		end
		PlanetOrbiting:PivotTo((CFrame.new(PlanetGettingOrbited.Position)*offset)*CFrame.new(x, y, z))
		Angle += DeltaTime*TAU/TimeToOrbit
	end)
	--HeartbeatConnection:Disconnect()
	-- Return the heartbeat connection, so we can disconnect it if we no longer wants the part to orbit
	return HeartbeatConnection
end

so if you put eccentricty as 15 it would look like this
image
But the thing with this is that the planet is moving at a constant speed but in real life planets move faster as they are closer to their parent object like this


this is how my code works in game
the speed is same on all places which isnt what i want

Any idea on how to create that?
Thanks!

1 Like

If you want real life behavior you should look at real life equations which account for acceleration and velocity. Seems like someone else has already done it:

Or you could just let the physics engine do it for you with a line force and such.

i mean i could but i really suck at math especially stuff like this complicated

2 Likes

you can use Magnitude to speed thing up

Do you only want the orbits to be realistic for 2-body systems? Or do you need it to also work with 3 or more bodies?

How tho ik magnitude gives me a distance between two points right?

1 Like

just two bro i want to create the most least complicated orbit system that has almost all the things that real orbit has

the eccentricty works but the speed is constant all times which should happen cuz the closer you the faster you go

1 Like

If you really want the simplest possible simulation, you can use numerical integration to simulate the system. This has the advantage that it works for any number of bodies, not just 2. In fact AFAIK it’s the only approach that works for more than 2 bodies.

Each body is just a particle that follows Newton’s laws of motion, and where the acceleration on a particle is given by Newton’s law of gravitation.

The specific equations we need are

a = F / m

ds = dv * dt, s being the position of a body and dt being the change in time (the “time step”).

dv = a * dt

and

F = G * (m1 * m2) / r^2, G being the gravitational constant.

How the simulation works is explained here:

Basically, you simulate the system in small time steps. Each step, you change the position of each body by it’s speed * dt, you change the velocity by acceleration * dt, and you set the acceleration on a body to be the sum of forces on that body due to gravity from the other bodies.

In code, that looks like so:

local G = 1 --tune this to get a simulation that "feels right".

local bodies = {}

function newBody(mass: number, p0: Vector3, v0: Vector3)
    local body = { mass = mass, position = p0, velocity = v0 }
    table.insert(bodies, body)
    return body
end

function getBodyGravitationForce(body): Vector3
    local forceSum = Vector3.new() 
    for _, body2 in ipairs(bodies) do
        if body2 == body then continue end --Bodies don't attract themselves. Also distance would be NaN, breaking everything.
        --Newton's law of universal gravitation
        local distance = (body.position - body2.position).Magnitude
        forceSum += G * body.mass * body2.mass / (distance * distance)
    end
    return forceSum
end

function stepSimulation(dt)
    --Calculate new positions and velocities for each body
    local newPositions = {}
    local newVelocities = {}
    for _, b in ipairs(bodies) do
        --Newton's laws of motion
        local acceleration = getBodyGravitationForce(b) / b.mass
        newVelocities[b] = b.velocity + acceleration * dt
        newPositions[b] = b.position + b.velocity * dt
    end
    
    --Apply new positions and velocities
    --This is done *after* simulating *all* bodies. 
    --Otherwise changes to one body would affect calculations for the bodies that come after
    for _, b in ipairs(bodies) do
        b.velocity = newVelocities[b]
        b.position = newPositions[b]
    end
end

local v3 = Vector3.new

newBody(10, v3(0,  0, 0), v3(0, 0, 0))
newBody(1,  v3(10, 0, 0), v3(0, 0, 8))

game:GetService("RunService").Stepped:Connect(stepSimulation)

I haven’t tested this so there might be some bugs or typos, but the overall idea works.

This way has some disadvantages if you just want 2 bodies. First, if you want to know the position of particles at some point in time then have to simulate all the way to that point in time. Second, real life motion is continuous and not discrete (it doesn’t move forward in steps). This causes some inaccuracy in the simulation which accumulates over time, meaning you’ll get wildly inaccurate results after a while. The smaller the time step the better the simulation. If you want smaller time steps without making the sim run slower, you can step it forwards e.g. 10 times every Stepped, but only do it by dt / 10 each step. Of course at some point the CPU will be unable to keep up so there’s still a limit for the number of bodies you can simulate and/or the precision with which you can do it.

Hope this helps!

3 Likes

in the getBodyGravitationForce function im getting this error

ServerScriptService.Script:17: attempt to perform arithmetic (add) on Vector3 and number

the code

forceSum += G * body.mass * body2.mass / (distance * distance)

Try

local direction = (body2.position - body.position).Unit
forceSum += direction * G * body.mass * body2.mass / (distance * distance)

btw i forgot to ask how will i make a part go in orbit with this?

1 Like

This was my script btw which had the speed of orbit problem
Orbit.rbxl (43.5 KB)

How can a function(or variable) that just returns the distance between 2 points speed it up?

i think he meant i could check the distance of the two part and somehow speed it up when its closer but the thing is i dont even know how to speed it up lol

Magnitude is a property from the vector3 class

Okay, but the property does not define the speed

I know that I’m just correcting you.

1 Like

I couldn’t understand, but magnitude doesn’t define the speed so it won’t work

1 Like

why are you fixing that problem with it if you have a solution to my problem just say it or just dont talk at all not being rude here

This is the stupidest answer you will get, but I think you want the object to slow down at the left, and move faster at the right

time it, or be synchronous and check if the object has reached a certain distance