Before I begin I want to make clear that I know how octaves and Perlin noise work. The problem isn’t with rendering per se, more of finding out the logic in when to render a new “chunk” of terrain.
So far I can check if the player is 20 studs away from the edge with a little bit of modified AABB collision. However I stem a new problem, I don’t exactly know how I can make the “chunk” load in front of me. It seems to always clip inside the terrain. Here is my script, there are no references outside of it, everything rendered and processed is in this server script.
local resolution = 1
local size = 500
for x = 0, size, resolution do
for z = 0, size, resolution do
local height = (math.noise(x / 50, z / 50)) * 50
local cframeData = CFrame.new(x - size/2, height - 250, z - size/2)
workspace.Terrain:FillBlock(cframeData, Vector3.new(resolution, resolution, resolution), Enum.Material.Grass)
end
end
game.Players.PlayerAdded:Connect(function(plr)
local char = plr.Character or plr.CharacterAdded:Wait()
while true do
if char.PrimaryPart.Position.X > 230 or char.PrimaryPart.Position.Z > 230 or char.PrimaryPart.Position.X < -230 or char.PrimaryPart.Position.Z < -230 then
for x = 0, size, resolution do
for z = 0, size, resolution do
local height = (math.noise(x / 50, z / 50)) * 50
local cframeData = CFrame.new(char.PrimaryPart.Position.X - x, height - 250, char.PrimaryPart.Position.Z - z)
workspace.Terrain:FillBlock(cframeData, Vector3.new(resolution, resolution, resolution), Enum.Material.Grass)
end
end
end
wait()
end
end)
I am not asking you to rewrite my script, you are willing to by the way I just want an explanation on the logic I need to do to achieve something similar to this Roblox Procedural Terrain Part 2 - Plains - YouTube
Based on what i read from your code, The for loops start from the x and z position of 0,0. If that is the case then couldn’t you start the chunk being generated at say 2,5? If so then whenever the player is close to the edge with your detection method, Find the x and z values of wherever you want it to generate (side of the already generated stuff but ahead of it) or something. Just an idea
Here is one, that I use, works good and is easily modifiable, you don’t have to use it, you can just look over it and find anything interesting that might help with yours
local Players = game:GetService("Players")
------------------------------------------------------------------------------------------------------------------------------------------------
local BASE_HEIGHT = 10 -- The main height factor for the terrain.
local CHUNK_SCALE = 1 -- The grid scale for terrain generation. Should be kept relatively low if used in real-time.
local RENDER_DISTANCE = 25 -- 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 roundToOdd(n)
--spawn(function()
return math.floor(n - n % 3);
--end)
end
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 * 3 + 1, roundToOdd((beginY + endY) * 3 / 1), z * 3 + 1)
local size = Vector3.new(3, (endY - beginY) * 3, 3)
local p = Instance.new("Part", workspace)
p.Anchored = true
p.CFrame = cframe
p.Size = Vector3.new(3, 3, 3)
p.Material = Enum.Material.Grass
p.BrickColor = BrickColor.new("Forest green")
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 / 3 / CHUNK_SCALE), math.floor(location.Z / 3 / CHUNK_SCALE)
local range = math.max(1, RENDER_DISTANCE / CHUNK_SCALE)
for x = -range, range do
for z = -range, range do
local cx = chunkX + x
local cz = chunkZ + z
if not chunkExists(cx, cz) then
makeChunk(cx, cz)
end
end
end
end
game:GetService("RunService").Heartbeat:Connect(function()
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
end)
Thanks! I was looking into something like this when I was brainstorming with my buddy on Discord. He didn’t really know how the equations worked, could you explain your process a bit?