I’m currently following Sebastian Lague’s terrain generation series and trying to translate it over to Roblox using EditableMesh, but I have these weird bumps that keep showing up whenever I generate terrain:
I’m not quite sure where the issue is coming from, but I have a suspicion that it might be because I’m layering multiple octaves to the point where it adds fine details that can’t be rendered well, like trying to approximate a smooth curve with just a few points.
Here’s my localscript:
-- SERVICES
local RS = game:GetService("ReplicatedStorage")
local AssetService = game:GetService("AssetService")
-- FUNCTIONS
local function GenerateMap(Length: number, Width: number, InitialAmplitude: number, InitialFrequency: number, Seed: number, Scale: number, Octaves: number, Persistence: number, Lacunarity: number, Offset: Vector2)
local RNG = Random.new(Seed)
local Offsets = {}
local EditableMesh = AssetService:CreateEditableMesh()
local VerticesStableIDs = {}
for i = 1, Octaves do
table.insert(Offsets, Vector2.new(RNG:NextNumber(-100000, 100000) + Offset.X, RNG:NextNumber(-100000, 100000) + Offset.Y))
end
local HalfWidth = Width / 2
local HalfLength = Length / 2
for X = 1, Width do
for Z = 1, Length do
local Amplitude = InitialAmplitude
local Frequency = InitialFrequency
local NoiseHeight = 0
for i = 1, Octaves do
local SampleX = ((X - HalfWidth) / Scale + Offsets[i].X) * Frequency
local SampleY = ((Z - HalfLength) / Scale + Offsets[i].Y) * Frequency
local Noise = math.clamp(math.noise(SampleX, SampleY), -0.5, 0.5) * 2 -- Generated between -0.5 to 0.5 for some reason (w/ exceptions)
NoiseHeight += Noise * Amplitude
Amplitude *= Persistence
Frequency *= Lacunarity
end
local VertexID = EditableMesh:AddVertex(Vector3.new(X - HalfWidth, NoiseHeight, Z - HalfLength))
table.insert(VerticesStableIDs, VertexID)
end
end
local VertexIndex = 1
for X = 1, Width do
for Z = 1, Length do
if X < Width and Z < Length then
EditableMesh:AddTriangle(VerticesStableIDs[VertexIndex], VerticesStableIDs[VertexIndex + 1], VerticesStableIDs[VertexIndex + Length + 1])
EditableMesh:AddTriangle(VerticesStableIDs[VertexIndex + Length + 1], VerticesStableIDs[VertexIndex + Length], VerticesStableIDs[VertexIndex])
end
VertexIndex += 1
end
end
local MeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(EditableMesh))
--{CollisionFidelity = Enum.CollisionFidelity.PreciseConvexDecomposition})
MeshPart.Anchored = true
MeshPart.CFrame = CFrame.new(0, 0, 0)
MeshPart.Parent = workspace
end
-- EVENTS
RS.GenerateTerrain.OnClientEvent:Connect(GenerateMap)
And here’s my serverscript:
-- SERVICES
local Players = game:GetService("Players")
local AssetService = game:GetService("AssetService")
local RS = game:GetService("ReplicatedStorage")
-- VARIABLES
local MapWidth = 100
local MapLength = 100
local Seed = 10--os.time()
-- EVENTS
Players.PlayerAdded:Connect(function(Player)
RS.GenerateTerrain:FireClient(Player, MapLength, MapWidth, 150, 1, Seed, 300, 6, 0.5, 2, Vector2.new(0, 0))
end)
For convenience, these are essentially my main parameters:
- Map width of 100 (number of vertices)
- Map length of 100 (number of vertices)
- Initial amplitude of 150
- Initial frequency of 1
- Seed is 10 (I’m keeping it fixed just for debugging purposes right now)
- Scale is 300
- The number of octaves is 6
- The persistence is 0.5
- The lacunarity is 2
The gap between each vertex is 1 stud. I’m not sure if this is too much or too little.



