# Issues with a terrain generation system that uses Hexagons

I’m relatively new to the DevForum, so I may have grammar or other issues!

1. What do you want to achieve? I want to make a terrain generator similar to Minecraft, but using Hexagons!

2. What is the issue? My generator script makes an error, namely:

attempt to compare number <= nil

1. What solutions have you tried so far? I am using @ThanksRoBama
s script from this post.

I modified their script so that it could work with hexagons, but it always return an error. this is the script:

``````local FractalNoise = require(game.ServerStorage.FractalNoise)
local heightmapNoise = FractalNoise(nil, 0.1, 2, 2, nil, nil) --These are just magic numbers
local swirlNoise = FractalNoise(nil, .01, 5, 2) --These are just magic numbers

function mapSet(map, x, y, z, value)
map[x] = map[x] or {}
map[x][y] = map[x][y] or {}
map[x][y][z] = value
end

function mapGet(map, x, y, z)
if map[x] then
if map[x][y] then
return map[x][y][z]
end
end
end

function twirlCoordinates(x, y, z, power)
local power = power or 1
local tX, tY, tZ =
swirlNoise(x, y, z),
swirlNoise(x+1000, y, z), --Don't want the *same* twirl on each axis
swirlNoise(x, y+1000, z)

return x + tX * power, y + tY * power, z + tZ * power
end

function heightMap(x, z)
return heightmapNoise(x, 0, z)
end

function density(x, y, z)
--If you twirl with power 0, you'll just get a plain heightmap
local tX, tY, tZ = twirlCoordinates(x, y, z, 1)
tZ = tZ / (1 + y)
tX = tX / (1 + y)
local densityOffset = 0.5 + heightMap(tX, tZ) - y --Add 0.5 density so that there's a guaranteed bottom layer
return densityOffset
end

function generateChunk(mapSize, offset)
local yMax = 8
local yMin = 1
print(offset)

local x
local y
local z

local densityMap = {}
for x = 1, mapSize do
for y = yMin, yMax do
for z = 1, mapSize do
mapSet(densityMap, x*offset["x"], y, z*offset["z"], density((x*offset["x"])/mapSize, y/(yMax), (z*offset["z"])/mapSize))
end
end
end

x, y, z = 0,0,0

for x = 1, mapSize do
for y = yMin, yMax do
for z = 1, mapSize do
local d = mapGet(densityMap, x*offset["x"], y, z*offset["z"])
--print(d, x*offset["x"], y, z*offset["z"])

if d >= 0 and d <= 0.14 then
local block = workspace.blockBase.Base_Plane:Clone()
--local block = Instance.new("Part", workspace)
--block.Size = Vector3.new(4, 4, 4)

if z % 2 == 0 then
x += 3.732 * 0.5
end
z *= 3.732 * 0.75

block.Anchored = true
block.CFrame = CFrame.new((x*offset["x"])*4, y*4, (z*offset["z"])*3.732)
block.Color = Color3.fromHSV(((y/mapSize)%1), .75, 1 - (y/mapSize))
block.Parent = game.Workspace
end

end

end
game["Run Service"].Heartbeat:Wait()
end
x, y, z = 0,0,0
end

generateChunk(16,
["x"] = 1,
["z"] = 1
})

``````

If you need the module, just get it from the link I gave.

Also, if you have questions, feel free to ask!

5 Likes

Can you send the complete error message?

3 Likes

Here’s the error message. I’m sorry I’m late to reply!

ServerScriptService.Script:68: attempt to compare number <= nil - Server - Script:68
Stack Begin - Studio
Script ‘ServerScriptService.Script’, Line 68 - function generateChunk - Studio - Script:68
Script ‘ServerScriptService.Script’, Line 93 - Studio - Script:93
Stack End - Studio

If you want the file, I can give it to you here: terrain_generator_with_hexagons.rbxl (59.6 KB)

3 Likes

mapGet returns nil when x/y/z is a float and when i tested thats what happens, returns nil when x is 2.866

or when somehow it accesses an index higher than the map has

1 Like

so i dont know if you figured it out yet, but in case you didnt:
in generateChunk, in the `if z % 2 == 0 then` replace x += … with z +=

1 Like

I’ll try than now! Thanks for the tip!

1 Like

That helped, but it makes weird terrain. I’ll post an image of it.

Better camera angle:

1 Like

can you show how you want it to look like? draw an image maybe?

1 Like

This, but with more variation (terrain features)

1 Like

i guess, try changing the offsets so they are more connected

1 Like

That didn’t work. I tried. Anything I did messed up the positions of the hexagons.

1 Like

i don’t know, i tried making the offsets 0.5x smaller or so and they looked more realistic, i guess you need to experiment with sizes maybe?

1 Like

I did get it to do something, but it’s not enough.

The script is now:

``````local FractalNoise = require(game.ServerStorage.FractalNoise)
local heightmapNoise = FractalNoise(nil, 0.1, 2, 2, nil, nil) --These are just magic numbers
local swirlNoise = FractalNoise(nil, .01, 5, 2) --These are just magic numbers

function mapSet(map, x, y, z, value)
map[x] = map[x] or {}
map[x][y] = map[x][y] or {}
map[x][y][z] = value
end

function mapGet(map, x, y, z)
if map[x] then
if map[x][y] then
return map[x][y][z]
end
end
end

function twirlCoordinates(x, y, z, power)
local power = power or 1
local tX, tY, tZ =
swirlNoise(x, y, z),
swirlNoise(x+1000, y, z), --Don't want the *same* twirl on each axis
swirlNoise(x, y+1000, z)

return x + tX * power, y + tY * power, z + tZ * power
end

function heightMap(x, z)
return heightmapNoise(x, 0, z)
end

function density(x, y, z)
--If you twirl with power 0, you'll just get a plain heightmap
local tX, tY, tZ = twirlCoordinates(x, y, z, 1)
tZ = tZ / (1 + y)
tX = tX / (1 + y)
local densityOffset = 0.5 + heightMap(tX, tZ) - y --Add 0.5 density so that there's a guaranteed bottom layer
return densityOffset
end

function generateChunk(mapSize, offset)
local yMax = 8
local yMin = 1
print(offset)

local x
local y
local z

local densityMap = {}
for x = 1, mapSize do
for y = yMin, yMax do
for z = 1, mapSize do
mapSet(densityMap, x*offset["x"], y, z*offset["z"], density((x*offset["x"])/mapSize, y/(yMax), (z*offset["z"])/mapSize))
end
end
end

x, y, z = 0,0,0

for x = 1, mapSize do
for y = yMin, yMax do
for z = 1, mapSize do
local d = mapGet(densityMap, x*offset["x"], y, z*offset["z"])
--print(d, x*offset["x"], y, z*offset["z"])

if d >= 0 and d <= 0.14 then
local block = workspace.blockBase.Base_Plane:Clone()
--local block = Instance.new("Part", workspace)
--block.Size = Vector3.new(4, 4, 4)

if z % 2 == 0 then
z += 3.732 * 0.5
end
z *= 3.732 * 0.25

block.Anchored = true
block.CFrame = CFrame.new((x*offset["x"])*4, y*4, (z*offset["z"])*3.732)
block.Color = Color3.fromHSV(((y/mapSize)%1), .75, 1 - (y/mapSize))
block.Parent = game.Workspace
end

end

end
game["Run Service"].Heartbeat:Wait()
end
x, y, z = 0,0,0
end

generateChunk(16, {
["x"] = 1,
["z"] = 1
})
``````
1 Like

i just realized something, if z % 2 == 0 will make the offset 0.5 times less, and then also 0.25 times less, is that intended?

1 Like

I don’t know. However, I have another script that generates terrain like I want, but with no terrain features (no noise, 3d noise, etc.)

Here’s the script:
oldScript.lua (3.8 KB)

``````local tile = workspace.blockBase

--! change these values to match the X and Z size of your tile mesh.
local tileWidth = 3.732
local tileHeight = 4
local renderDistance = 1
local fogScreenSize = 16

local biomes = { Plains = {
name = "Plains",
resolution = 100,
frequency = 0.5,
amplitude = 10
},
Mountains = {
name = "Mountains",
resolution = 75,
frequency = 2.5,
amplitude = 10
}
}

local seed = -67865454874585

function getIfLife(x: number, z: number, y: number, biome, typeOfTerrain: string)
--fix generator
local xinter = (x/(biome["resolution"]*biome["frequency"]))
local xincrement = x

local zinter = (z/(biome["resolution"]*biome["frequency"]))
local zincrement = z

local noiseMap1 = math.clamp(math.noise(xincrement, zincrement, 1), -0.5, 0.5)*300
--local noiseMap2 = math.clamp(math.noise(xincrement*-4, -zincrement*4, seed)/4, -0.5, 0.5)*300
--local noiseMap3 = math.clamp(math.noise(xincrement*-16, -zincrement*16, seed)/16, -0.5, 0.5)*300

--3d noise
local noise3d1 = math.noise(xinter, zinter, y/biome["resolution"]*biome["frequency"])*biome["amplitude"]
local noise3d2 = math.noise(xinter*2, zinter*2, y/biome["resolution"]*biome["frequency"]*2)*biome["amplitude"]*0.5
local noise3d3 = math.noise(xinter*4, zinter*4, y/biome["resolution"]*biome["frequency"]*4)*biome["amplitude"]*0.25
local noise3d4 = math.noise(xinter*8, zinter*8, y/biome["resolution"]*biome["frequency"]*8)*biome["amplitude"]*0.125

local noise3dFull = noise3d1 * noise3d2 * noise3d3 * noise3d4

local noise = math.clamp(((noiseMap1*noise3dFull or 0) + (noiseMap2 or 0) + (noiseMap3 or 0))/3,-1,1)

local noise2 = math.noise(x*y,z*y,seed*y)
print(noise, math.clamp(noise*2,-0.1,0.1))
local output = math.clamp(noise*2,-0.1,0.1) > 0
--print(noise, noise2, output, typeOfTerrain)
return output
end

xPosition: number,
zPosition: number,
location: Model,
ypos: number,
hasHeight: boolean,
isDirt: boolean,
offsets,
biome,
isCaves
)
xPosition *= tileWidth

--? we offset the second row slightly by 1/2 the tile's width.
if zPosition % 2 == 0 then
xPosition += tileWidth * 0.5
end
zPosition *= tileHeight * 0.75

local tile = tile:Clone()
local terrainType = nil
if isCaves then
terrainType = "Cave"
else
terrainType = "Ground"
end

local lives = getIfLife(xPosition,zPosition,ypos,biome, terrainType)

if lives == false then
tile:Destroy()
return end

tile.Parent = location
end

function generateLayer(xoffset: number, y: number, zoffset: number, location: Model, biome, isCave: boolean)
local xPos1
local zPos1
for zPos = 1, fogScreenSize do
zPos1 = zPos
for xPos = 1, fogScreenSize do
xPos1 = xPos
AddTile(xPos+(xoffset*fogScreenSize), zPos+(zoffset*fogScreenSize), location, (y+8)*tileHeight, true, true, {["X"] = xoffset, ["Z"] = zoffset}, biome, isCave)
end
game:GetService("RunService").Heartbeat:Wait()
end

end

local zPos1
local xPos1

function generateChunk(xoffset: number, zoffset: number, biome)
local location = Instance.new("Model")
location.Parent = workspace.FogScreenObjects
location.Name = "(" .. xoffset .. ", " .. zoffset .. ")"

location:SetAttribute("Biome", biome.name)
for i = 1, 16 do
generateLayer(xoffset, i, zoffset, location, biome, true)
end
for i = 1, 4 do
generateLayer(xoffset, i+16, zoffset, location, biome, false)
end
end
for i = 1, renderDistance do
for o = 1, renderDistance do
local noise = math.noise(i,o,seed)
if math.clamp(noise, 0, 1) == 1 then
generateChunk(i,o, biomes.Plains)
else
generateChunk(i,o, biomes.Mountains)
end
end
end
``````
1 Like

well you can try changing that to

``````if z % 2 == 0 then
z += 3.732 * 0.5
else
z += 3.732 * 0.25 --or maybe 0.75?
end
``````

and see the results

1 Like

It is already set to that one. (The image is the result)

1 Like

no i mean, add the else condition

1 Like

Oh. My bad! I’m crazy sometimes!

1 Like

That kind of helps. But it, since I implemented this new version of my placement system, has always put two Hexagons in one spot (that is why rows are skipped).

1 Like