Smoothen Terrain

I want to make my terrain smoother. What I mean is that when I generate terrain using my script, it’s all bumpy compared to the Roblox article’s script. How can I achieve this smoothness? I can provide my code if needed.


my terrain


roblox’s terrain

local render = 200
local chunkheight = 50
local chunks = {}
local hillwidth = 100

local function makechunk(x, z)
	local y = math.noise(math.random(), x/hillwidth, z/hillwidth) * chunkheight
	local position = CFrame.new(x, 0, z)
	local size = Vector3.new(4 , (y + chunkheight), 4)
	workspace.Terrain:FillBlock(position, size, Enum.Material.Snow)
end

local function check(Root)
	for x = -render, render, 4 do
		local renderedx = math.floor(x + Root.Position.X)
		if not chunks[renderedx] then
			chunks[renderedx] = {}
		end
		for z = -render, render, 4 do
			local renderedz = math.floor(z + Root.Position.Z)
			if not chunks[renderedx][renderedz] then
				chunks[renderedx][renderedz] = {}
				makechunk(renderedx, renderedz)
			end
		end
	end
end

while true do
	for i, v in ipairs(game.Players:GetPlayers()) do
		if v.Character then
			if v.Character.HumanoidRootPart then
				check(v.Character.HumanoidRootPart)
			end
		end
	end
	wait(1)
end

my code

1 Like

i think your main problem is your not aligning the block with the terrain

try changing

local position = CFrame.new(x, 0, z)

to

local position = CFrame.new(x + 2, 0, z + 2)

but you can only go so far with Terrain:FillBlock

because you dont have control over the occupancy and you can run into problems when adding water

i would recommend using
https://developer.roblox.com/en-us/api-reference/function/Terrain/WriteVoxels

but then you have to calculate occupancy manually

if you edit this project

you will find a script in there that generates terrain and you can see how im using Terrain:WriteVoxels in there

but if your feeling lazy you could just use my plugin

3 Likes

Thanks for reply.

Tried that but my terrain is still rough. I’m sure I won’t be adding water but I’ll check out your plugin and view the script. I prefer not to use plugins that do the work for me otherwise I won’t learn anything.

I’ll try it with other values to see if that works.

here is a version that uses workspace.Terrain:FillBlock

local distance = 16
local chunkSize = 16
local updateDistance = 128
local positionX = math.huge
local positionZ = math.huge
local heightData = {}
local loaded = {}

GetHeight = function(x, z)
	if heightData[x] == nil then heightData[x] = {} end
	if heightData[x][z] ~= nil then return heightData[x][z] end	
	local height = 0
	height += math.noise(x * 0.01, 0, z * 0.01) * 40
	height += math.noise(x * 0.04, 0, z * 0.04) * 10
	heightData[x][z] = height
	return height
end

Load = function(x, z)
	local minimum = math.huge
	local maximum = -math.huge
	for xx = x-1, x+1 do
		for zz = z-1, z+1 do
			local height = GetHeight(xx, zz)
			minimum = math.min(minimum, height)
			maximum = math.max(maximum, height)
		end
	end
	local slope = maximum - minimum
	local height = heightData[x][z]
	local thickness = height - minimum + 6
	local position = Vector3.new(x * 4, height, z * 4)
	local cFrame = CFrame.new(x * 4 + 2, height - thickness / 2, z * 4 + 2)
	local size = Vector3.new(4, thickness, 4)
	if slope < 1 then
		workspace.Terrain:FillBlock(cFrame, size, Enum.Material.Mud)
	else
		workspace.Terrain:FillBlock(cFrame, size, Enum.Material.Sand)
	end
	if height >= 0 then return end
	thickness = -height
	local cFrame = CFrame.new(x * 4 + 2, height + thickness / 2, z * 4 + 2)
	local size = Vector3.new(4, thickness, 4)
	workspace.Terrain:FillBlock(cFrame, size, Enum.Material.Water)
end

LoadChunk = function(chunkX, chunkZ)
	if loaded[chunkX] == nil then loaded[chunkX] = {} end
	if loaded[chunkX][chunkZ] ~= nil then return end
	loaded[chunkX][chunkZ] = true
	local startX = chunkX * chunkSize
	local startZ = chunkZ * chunkSize
	local endX = startX + chunkSize - 1
	local endZ = startZ + chunkSize - 1
	for x = startX, endX do
		for z = startZ, endZ do
			Load(x, z)
		end
	end
	wait()
end

LoadChunks = function(chunkX, chunkZ)
	local x, z = chunkX, chunkZ
	local dx, dz = 1, 0
	local passed, length = 0, 1
	LoadChunk(x, z)
	for i = 1, (distance * 2 + 1) * (distance * 2 + 1) - 1 do
		x += dx z += dz
		if Vector2.new(chunkX - x, chunkZ - z).Magnitude < distance + 0.5 then LoadChunk(x, z) end
		passed += 1
		if passed == length then passed = 0 dx, dz = -dz, dx if dz == 0 then length += 1 end end
	end
end

workspace.CurrentCamera:GetPropertyChangedSignal("Focus"):Connect(function()
	local x = workspace.CurrentCamera.Focus.Position.X
	local z = workspace.CurrentCamera.Focus.Position.Z
	if Vector2.new(positionX - x, positionZ - z).Magnitude < updateDistance then return end
	positionX, positionZ = x, z
	LoadChunks(math.floor(x / 4 / chunkSize), math.floor(z / 4 / chunkSize))
end)
1 Like

Thanks for the reply.

Are you able to explain how the script works and why you divided by 4 in certain parts?
Also how did you make the terrain so smooth?

so you can think of the terrain like this

image

and each cube is 4x4x4 studs in size so thats why im dividing by 4

the main part of that code that you need to look at is

local cFrame = CFrame.new(x * 4 + 2, height - thickness / 2, z * 4 + 2)
local size = Vector3.new(4, thickness, 4)
workspace.Terrain:FillBlock(cFrame, size, Enum.Material.Mud)

but i dont recommend doing it with workspace.Terrain:FillBlock

in this project you will find the better workspace.Terrain:WriteVoxels method that i used

and this better method allowed me to get smooth underwater terrain but it might even might effect terrain above water a little

2 Likes

Why did you +2 when you multiply by 4?
Also if terrain is on a 4x4x4 grid then what happens if you create terrain with a 1, 1, 1 size?

maybe this will help

here you can see the terrain occupancy
image

each grid is 4 by 4 studs

and if the grid is full then the occupancy is set to 1
if the grid is 50% full then the occupancy is set to 0.5

you cant have terrain that is 1 by 1 in size you can have a 4 by 4 with a low amount of occupancy that will make it feel like a 1 by 1 but its a 4 by 4 with a low occupancy

the reason i did +2 is to align the fillblock with the 4 by 4 grid so that it will set the occupancy to 1 if you are not aligned then the occupancy will be wrong


and here you can see what it looks like as the occupancy gets lower

2 Likes

Thanks for the explanation. It makes sense.

Though I still don’t know why my terrain is still rough. Do you have any ideas?

it might be the material your using try with sand and see if you get the same problem

1 Like

the problem persists with sand as well

then the only thing i can think of it that your not aligning the workspace.Terrain:FillBlock with the grids of the terrain correctly

1 Like

What can I do to fix it then??

so the problem is most likely

 local renderedx = math.floor(x + Root.Position.X)

i think this is not positioning the block correctly

you can use

… -6, -2, 2, 6, 10, 14 …
these are all positions you can use for workspace.Terrain:FillBlock

1 Like

I try that and then get this. u can see from the output that all the values are 4n-2. i can send my code

image
grass version

--[[]]
local render = 200
local chunkheight = 40
local chunks = {}
local hillwidth = 2
local fix = 2

local function makechunk(x, z)
	local y = math.noise (math.random(), x/hillwidth*4, z/hillwidth *4 ) * chunkheight
	x = x * 4 + 2
	z = z * 4 + 2
	print(x, z)
	local position = CFrame.new(x, 0, z)
	local size = Vector3.new(4 , (y + chunkheight), 4)
	workspace.Terrain:FillBlock(position, size, Enum.Material.Grass)
end

local function check(Root)
	for x = -render, render do
		local renderedx = math.floor((x + Root.Position.X)/4)
		renderedx = renderedx
		if not chunks[renderedx] then
			chunks[renderedx] = {}
		end
		for z = -render, render do
			local renderedz = math.floor((z + Root.Position.Z)/4)
			if not chunks[renderedx][renderedz] then
				chunks[renderedx][renderedz] = {}
				makechunk(renderedx, renderedz)
			end
		end
	end
end

while true do
	for i, v in ipairs(game.Players:GetPlayers()) do
		if v.Character then
			if v.Character.HumanoidRootPart then
				check(v.Character.HumanoidRootPart)
			end
		end
	end
	wait(1)
end

Have tried without hillwidth * 4 same result.

Ok I see another problem

Your using math.random inside the math.noise function you don’t want to do that

Change math.random to 0

1 Like

Thanks alot
u were very helpful.

this especially helped. thanks!
my terrain is now smooth as butter.

Nice I’m happy you got it working

1 Like