Hi
I have a simple procedural terrain generation script that works well and all but there’s a problem with how the generation looks. Border between chunks looks 99% identical aka straight line going at an angle shown here:
(red line shows border between biomes)
How would I make it more diverse?
Picture to further explain what I mean:
On the left chunks (red squares) go into the other biome slightly so that they “mix up” a bit while on the right side they are all in one line (how it is now) clearly showing where the border between two biomes is instead of trying to blend them together for a smoother transition between biomes.
Code I’m using to generate the map:
-- Returns world table (table) --
function chunkSystem:generateWorld(quadrantSize)
local worldTable = {}
local total = ((quadrantSize * 2) + 1) * ((quadrantSize * 2) + 1)
local allIndex = 0
local index = 0
for x = -quadrantSize, quadrantSize do
-- create new row --
worldTable[tostring(x)] = {}
for z = -quadrantSize, quadrantSize do
-- assign chunk and chunk orientation --
local biome = getBiome(math.noise(SEED, x/chunkSystem.biomeSmoothness, z/chunkSystem.biomeSmoothness) / 1.1 + 0.5)
worldTable[tostring(x)][tostring(z)] = {getRandomChunk(biome), getRandomChunkAngle()}
allIndex += 1
index += 1
if index > 10000 then
index = 0
print("Generating: " .. tostring(math.floor(allIndex/total * 100)) .. "%")
wait()
end
end
end
print("Finished: " .. tostring(allIndex))
return worldTable
end
It is just 1 function of it but that’s the main one which generates the terrain for local player. Should be easily readable. (rootPart - local player’s humanoid root part, worldTable - table with all the info about which chunk is where generated by the server)
What method are you using to generate the terrain? There are many ways to go about this, but it’s hard to find one that is right for you without any code or information to go off of.
1 Like
I’ll attach a code snippet as soon as I get on my PC.
Edited the main post, now it has a code snippet.
That’s the code for loading / unloading chunks. You need to post either the code for createChunk
or the method you’re using to generate chunks.
1 Like
My bad, I updated the post.
(3O chars)
Is getBiome
checking a table and pulling the closest biome value depending on the input? Or is there something more to that function?
I can’t say much about how you’re using this values to create the biomes, but there are some things that I can suggest based on the generateWorld
function.
First thing I can recommend is changing the parameters passed into math.noise
.
Instead of
math.noise(SEED, x/chunkSystem.biomeSmoothness, z/chunkSystem.biomeSmoothness)
, I would do
math.noise(SeedX * x / chunkSystem.biomeSmoothness, SeedZ * z / chunkSystem.biomeSmoothness)
. Having two different seed values will help prevent you from generating two different areas of the world with the exact same noise values.
Another thing I would suggest is adding multiple octaves of noise instead of using just one. What I mean by this is that you only call math.noise
one time, which is fine on its own, but adding multiple octaves of noise together at different frequencies will vary the terrain much more.
Instead of
local biome = getBiome(math.noise(SEED, x/chunkSystem.biomeSmoothness, z/chunkSystem.biomeSmoothness) / 1.1 + 0.5)
, I would do something like
local noiseX, noiseZ = SeedX * x / chunkSystem.biomeSmoothness, SeedZ * z / chunkSystem.biomeSmoothness
-- Generate different Octaves of noise at different frequencies each
local noise = math.noise(noiseX, noiseZ) +
0.50 * math.noise(2 * noiseX, 2 * noiseZ) +
0.25 * math.noise(4 * noiseX, 4 * noiseZ)
-- Normalise the noise values back to what is normally outputted by math.noise
noise = noise / (1 + 0.50 + 0.25 + 0.125)
Besides implementing these suggestions, I would recommend you take some time to change the numbers you’re using to generate the terrain. Keep playing with them until you reach a spot that you are happy with.
1 Like
Thank you so much for a detailed explanation, I implemented your suggestions and played around with the numbers but no matter what I do it looks really scattered. There’s one biome which is the most common one (furniture) and in this biome I can see like 2-5 chunks for another “close to it” biome. Any idea how would I improve it so biome stayed more together instead of being scattered around?
Also in these lines:
0.50 * math.noise(2 * noiseX, 2 * noiseZ) +
0.25 * math.noise(4 * noiseX, 4 * noiseZ)
I don’t understand what the 0,50, 2, 0.25 and 4 is meant to be.
When you do 2 * noiseX, 2 * noiseZ
, you are generating another octave a noise that is at a different point. Because it’s so far away from your current noise position, it has a higher frequency, which would make every look really scattered if you kept it as is. However, because we lower its value by multiplying it by 0.50, it helps to add some variety to the noise generated, but not cause it to be absolute chaos.
Keeping certain biomes close together has always been a difficult process for me. I have some ideas, but they might not be relevant depending on how you’re determining biomes based on noise.
One idea might be to change up the values you’re using to determine the biomes, assuming it’s just a simple lookup table. For this, I would keep track of the noise values that have been generated and add them all up and divide by the total number in order to see the average value of noise. Using the average value, you could then make the most common biome close to that value and make rarer biomes further and further away.
You could also introduce another noise value to help change up the biome generation. Right now you only have one noise value doing all the work, you could say that value is Moisture and then introduce another value for Temperature. Using both of these, you could create a more complex table for determining the biome.
This is a visualized lookup table that I created for one of my past projects that worked fairly well. It would just be a matter of creating a table with the min and max values for both moisture and temperature and iterating through that to see which biome has been picked.
There is also the matter of scale. Noise is a continuous function that generates similar values close to one another. I would go back and take SeedX * x / chunkSystem.biomeSmoothness
and change it to something like SeedX * x / chunkSystem.biomeSmoothness * Scale
, with scale being somewhat small value. Some of my projects have used 0.25 as a scale, while other projects have gone as low as 0.005. This value will heavily depend on how big you want the biomes to be, with larger scale values making biomes much smaller.
If you need more help, let me know.
1 Like
Thank you for helping so much bro. I applied the Scale variable and played around with numbers. Also the problem I reported before was a dumb mistake, I forgot this chunk was in that biome which make me think biomeB had scattered chunks in biomeA. The problem that I now have is no matter how far I go I can’t seem to find a different biome than the most common one, every single time. I even changed the Scale variable to something like 100, even 1000 instead of 0.005. Could you please explain what I could be doing wrong?
I think it might have something to do with how I clamp the noise and get biomes out of it, because it was took from my older project and was explained to me by someone else.
-- Returns biome name (string) --
local function getBiome(bV)
local biomeVal = math.clamp(bV,0,1)
for biome, data in pairs(biomeData) do
if inNumberRange(biomeVal, data.biomeValRange) then
return biome
end
end
end
``
Also In my older project I played around with temperature and moisture noise which turned out good and all but I don't think it's suited for this project. It's meant to generate IKEA looking map not a "realistic" environment.
Thanks in advance.
I wouldn’t clamp the noise like that, or at least - not immediately. math.noise
returns values on a range of [-1, 1], your clamp will shift half the data towards 0. If you want it on a range of [0, 1], then I would do something like :
local biomeVal = (bV + 1) / 2
It is possible for the returned noise value to be outside of the usual [-1, 1] range, so I would clamp it after shifting up the range - assuming it is very important for it to be within [0, 1].
I’m not sure exactly what inNumberRange
is doing, but I assume it’s just checking to see if the noise value is within a range of the biome value. You might want to try to find the biome which is closest to the noise value and return that, rather than returning the first biome that matches. Although, I’m not sure how big of a difference that would make.
I would recommend you change up your biome values if the change I recommended doesn’t make a difference.
1 Like
Tried it out right now, and now I can see all the biomes generate. It’s very scattered but that’s just the case of playing around with the values. Thank you so much for helping and explaining everything so well bro. It’s really hard to find people like you who know some much about this stuff on roblox.
1 Like
You’re welcome. Good luck on your project and let me know if you need more help in the future.
1 Like