If you take a look at Roblox’s article on terrain programming, you will get introduced to useful information for what you’re trying to achieve.
They also include, at the very bottom of the article, a pre-made module for procedural terrain generation. Here:
local Players = game:GetService("Players")
------------------------------------------------------------------------------------------------------------------------------------------------
local BASE_HEIGHT = 10 -- The main height factor for the terrain.
local CHUNK_SCALE = 3 -- The grid scale for terrain generation. Should be kept relatively low if used in real-time.
local RENDER_DISTANCE = 120 / 4 -- The length/width of chunks in voxels that should be around the player at all times
local X_SCALE = 90 / 4 -- How much we should strech the X scale of the generation noise
local Z_SCALE = 90 / 4 -- How much we should strech the Z scale of the generation noise
local GENERATION_SEED = math.random() -- Seed for determining the main height map of the terrain.
------------------------------------------------------------------------------------------------------------------------------------------------
local chunks = {}
local function chunkExists(chunkX, chunkZ)
if not chunks[chunkX] then
chunks[chunkX] = {}
end
return chunks[chunkX][chunkZ]
end
local function mountLayer(x, heightY, z, material)
local beginY = -BASE_HEIGHT
local endY = heightY
local cframe = CFrame.new(x * 4 + 2, (beginY + endY) * 4 / 2, z * 4 + 2)
local size = Vector3.new(4, (endY - beginY) * 4, 4)
workspace.Terrain:FillBlock(cframe, size, material)
end
function makeChunk(chunkX, chunkZ)
local rootPosition = Vector3.new(chunkX * CHUNK_SCALE, 0, chunkZ * CHUNK_SCALE)
chunks[chunkX][chunkZ] = true -- Acknowledge the chunk's existance.
for x = 0, CHUNK_SCALE - 1 do
for z = 0, CHUNK_SCALE - 1 do
local cx = (chunkX * CHUNK_SCALE) + x
local cz = (chunkZ * CHUNK_SCALE) + z
local noise = math.noise(GENERATION_SEED, cx / X_SCALE, cz / Z_SCALE)
local cy = noise * BASE_HEIGHT
mountLayer(cx, cy, cz, Enum.Material.Grass)
end
end
end
function checkSurroundings(location)
local chunkX, chunkZ = math.floor(location.X / 4 / CHUNK_SCALE), math.floor(location.Z / 4 / CHUNK_SCALE)
local range = math.max(1, RENDER_DISTANCE / CHUNK_SCALE)
for x = -range, range do
for z = -range, range do
local cx, cz = chunkX + x
local cz = chunkZ + z
if not chunkExists(cx, cz) then
makeChunk(cx, cz)
end
end
end
end
while true do
for _, player in pairs(Players:GetPlayers()) do
if player.Character then
local humanoidRootPart = player.Character:FindFirstChild("HumanoidRootPart")
if humanoidRootPart then
checkSurroundings(humanoidRootPart.Position)
end
end
end
wait(1)
end
I suggest playing around with this code and ultimately forking it in order to get exactly what you’re looking for, as this is the closest thing to it. There are, however, two problems I can think of right now that might get in the way of your development:
1). Completely randomized generation. If a player wishes to return to a certain region on a planet after the server was unloaded, they’d fail to find it (though depending on your game style it can be a benefit to ensure luck and prevent players memorizing advantageous areas of the map).
2). I’m inferring that there would be choppy terrain when two different chunks are being loaded by two players coming together.
One other aspect that would be difficult to master is gravity and space altering if you go with a No Man’s Sky type open world game with spherical planets that load randomly upon reaching and saving player plots on randomized terrain. Lots of things to look into but I wish you the best of luck!