# How to reduce speed based on distance from planet

So I am working on a space game, and to travel around solar systems, you can enter a mode called ‘subspace’, which allows your ship to travel at up to 10 million studs per second.

However, when the ship is close to planets, I don’t want the ship to be able to go that fast, as it would ruin the gameplay experience. I am trying to replicate, or at least make something similar to Elite Dangerous’s way of slowing down the ship.

Basically, the closer you are to celestial bodies, the slower your ship can travel because the subspace thrusters have to work harder against gravity to bend space-time.

So I was wondering how I can create this effect. I have experimented with the equation `F = G(m1*m2)/r^2` to get the gravitational force on the ship, but the problem is that I don’t know how to apply this to lowering the ship’s max speed.

I know that I can use magnitude, and I did use this before, but it didn’t work with multiple planets, and I would like something realistic.

I am not asking for the entire code for this system, but rather a way to go about achieving the effect.

Any solutions are greatly welcomed!

That should be it. The only trick is with multiple planets.

For this you can take the vector sum of the forces summing up the force between planet 1 and ship, planet 2 and ship, planet 3 and ship, … and so on. The direction vector will be (planet1.Position - ship.Position) going towards the planet since planets usually have higher mass.

This vector sum magnitude should be zero if the forces cancel out and potentially infinite if very close to a planet I believe assuming point mass.

To optimize further you can use Octrees to get the closest planets which have an significant effect. The furthest planets will have a very high r value where the force close to zero.

2 Likes

idk why everyone jumps to octrees for optimizing things like this, spatial hashing is vastly more flexible and performant, and easier to implement

2 Likes

But the output of this equation is not related at all to the ships maximum speed. If, for example, I do `10_000_000 - F`; if I am close enough to the planet, the speed goes into the negative, and yes I could just clamp it, but then the ship would be going way too slow to even notice that you are getting closer to the planet.

1 Like

Since this is a “made up” force I think it would be cool to treat this kind of force as a drag force caused by bent spacetime. You could use the equation for drag here and replace `density` with the how “bent” spacetime is. You could optionally remove the reference area of the equation or replace it with something else like mass.

To use this you’d have an equation to calculate how bent spacetime is for a given position (like the equation you showed in your original post), and then plug that into this equation to get a resisting drag force, and just subtract that (factoring deltatime) from the ship’s current velocity.

Since this is based on a real world force the results should feel natural.

2 Likes

While this is a good idea, I would rather focus on the kind of solution that @dthecoolest suggested, just because of the fact that I find it more suited to my needs. However, I will still give your idea a go.

1 Like

For this other problem you can use number mapping as well. You have already obtained a number related to the number of planets and a distance and now just need to adjust and transform it.

This is not directly physics related but more so gameplay related and so you can make up the numbers basically.

For example the force magnitude range should be [0, infinity], you can clamp it to [0, 10000] then use a mapping function to transform that into [0, 0.25] a decimal percentage factor to reduce speed.

For @WreckDough thanks never knew about spatial hashing.

I believe the reason for your query is that Octrees is probably more popular on Roblox because other developers have implemented it and published them as a nicely formatted community resource such as below:

2 Likes

So basically what you are suggesting is:

1. Get the gravitational force that all the planets are having on the ship
2. Merge/add those gravitational forces together
3. Use the map function to get a percentage of the maximum speed based on the gravitational force
4. Apply that percentage to the ship’s max speed

E.g:

Planet1 has 500,000N of force on the ship
Planet2 has 250,000N of force on the ship
Total force on ship: 750,000N
10,000,000 - 750,000 = 9,250,000
Max speed = 9,250,000

Please correct me if I am wrong, because I am still a bit confused on what map() does.

1 Like

Potentially it could be set up with:

• Saving the celestial objects under a list
• Iterating through the list with index and named variable of your space object
• Applying each of the magnitude for your equation relative to which object was chosen from where it was iterated
• Use RunService.RenderStepped to repeat prior steps indefinitely (and with good performance)
1 Like

Only very briefly skimmed this thread because my Octree post was linked, just wanted to say that I am unsure how exactly your game will function but if you actually mean 10 million studs per second, I’m going to tell you right now you’re going to run into issues.

The problem is that in Roblox (and many other engines for that matter), the further from 0,0,0 you go, physics precision gets worse. Past about 40,000 studs this becomes very noticeable and once you get several hundred thousand studs away it’s totally unplayable. Case in point: You will need to use trickery that moves the enviornment and not the player when fast traveling.

Example: The ｆｌｏａｔｉｎｇ ｐｏｉｎｔ zone - Roblox

(Images of the phenomenon)

I basically move the player and their ship backward back to 0,0,0 every 35k studs that they travel, and it works perfectly at up to speeds of 20 million studs per second, with virtually no frame drops (I run a solid 59-60fps traveling at 20M studs/second)

1 Like

Ah, smart technique!

If you are going to use the Octree system, be aware it is not intended to function with large distances, so you’d want to use it just for that 35,000 stud area. Also if all you’re trying to do is get distance to the planet, octrees would not be the right solution for that specific problem - instead you would want to do some basic math so you could simulate the distance. If it makes it easier, you could say that instead of storing the distance in studs, you could store the distance in Kilostuds or Megastuds or whatever. Point being, unless you have a thousand planets, it’s easier to just use basic magnitude math.

I am currently looking into the map() technique to change the Newton force into a speed, by first clamping the force to the magnitude between the ship and the planet, and then changing that into a speed clamped at 20_000_000. Not completely sure about everything yet, but it seems to be going alright.

Thank you for your response, if you have anything else that could help, please don’t hesitate to tell me, as I am still a bit confused.

Do you have a video of the effect you are intending to achieve? I am not an uber math programmer but I have learned over time that there is usually a better way to do things like this, typically by faking the effect. For example, instead of calculating the actual force in newtons, have you considered doing something more basic like checking if the planet is within a certain threshold distance, and then reducing the speed on a curve based on distance to the planet, multiplied by the planet’s relative size?

I couldn’t find a video without talking, but here is kind of an example of what I mean:

EDIT: His speed is shown at the top right of the minimap in the center console

As you can see, when he enters supercruise, his speed is very slow, because he is close to a planet, but as he escapes that planet’s gravitational pull, he speeds up to `C`, which is the speed of light if you didn’t know. And then, as he gets closer and closer to his destination, he starts slowing down again, into MM/s.

And when I say “he speeds up”, I don’t mean that he is purposefully speeding up as he gets further from the planet, the game is forcing him to speed up because the gravity acting upon him is weaker.

Ah, I see. Instead of trying to calculate real-world exact values, what if you instead just did something close enough?

What if you had a threshold distance (influenced by planet’s size), and if you are within that threshold, exponentially decrease the speed (potentially influenced by planet’s size) until you hit a minimum value?

Too lazy to do this right now but here is some code I generated with chatGPT and modified a bit that seems to work mostly when i did a quick test.

``````-- A function that returns the adjusted speed based on distance, original speed, min speed and threshold
-- If distance is greater than or equal to threshold, return original speed
if distance >= threshold then
return original_speed
end
-- Otherwise, calculate the ratio of distance to threshold
local ratio = distance / threshold
-- Use an exponential function to decrease the speed as ratio approaches zero
local factor = math.exp(-10 * (1 - ratio))
-- Return the speed as a linear interpolation between min speed and original speed, scaled by factor
return min_speed + (original_speed - min_speed) * factor
end

-- A function that tests the adjust_speed function with different distances
local distance = threshold + 10000
-- Loop until distance is slightly below zero

local inc = 1000; -- 1000
while distance > 0 do
-- Print the distance and the adjusted speed
print(string.format("Distance: %d, Speed: %d", distance, adjust_speed(distance, original_speed, min_speed, threshold)))
-- Decrease the distance by 10000
distance = distance - inc
end

while distance < threshold+10000 do
-- Print the distance and the adjusted speed
print(string.format("Distance: %d, Speed: %d", distance, adjust_speed(distance, original_speed, min_speed, threshold)))
-- Decrease the distance by 10000
distance = distance + inc
end
end

-- Test the functions with the given values
``````

Thank you for the response. I think I have almost figured out how to do it with the actual gravitational equation.

I just tested this a couple of minutes ago and it seemed to work almost perfectly:

``````local radius = (planetTransform.OriginalScale.Value.X)/2

local volume = (4/3) * math.pi * (radius^3)
local density = planetTransform:FindFirstChild("Density").Value
local mass = volume*1000 * density

local distance = (planetTransform.OriginalPosition.Value - shipValues.ship.PrimaryPart.Position).Magnitude

local force = ((distance)^2/((6.67 * 10^-11) * mass * 564655))

--print(force)

--20_000_000 is max speed, 5000 is made up number that just works
local result = 20000000 - ( 20000000 - ( (5000*radius*2) * force ) )

--200 is minimum speed
local finalResult = easyFunctions:map(result, 0, distance, 200, 20000000, true)
print(finalResult)
average += finalResult
``````

I basically loop through all the planets that the ship is within 10_000 radiuses of, then I use the opposite of Newtons law, so that the value is bigger when further away. After that, I take away that value from 20M with some other numbers, and finally, I map that to be within 20M.

However, some weird things do happen sometimes, but hopefully, I can fix them, and I still need to decide on what to replace `5000` with so that the game is balanced.

1 Like

What sorta weird things? ­­­­­­

Nevermind, I just made a silly mistake of forgetting to place a planet in the planet’s folder, which caused the force output to be `NAN`.

There is still one issue, which is less of an issue and more of a game-balancing thing. Basically, if you are getting closer to a planet and you reduce your speed manually, it takes a long time for the speed to decrease, because of how I have set up my ship flying system. And sometimes, if you are going too fast when getting closer to a planet, the ship’s speed will exceed the new max speed.

As I said though, those are things that I want to happen. I want the player to be able to ‘overshoot’ their destination and have to turn around, but it’s just a matter of making sure that they don’t overshoot every time.

Here is the ‘final’ product that I have come up with, it seems to work perfectly, but I’m sure that will change: