(1st post didn’t get replies…)
I’m trying to make a multiplayer survival game, with randomly generated worlds, and randomly generated resources and buildings.
What I need help with here is making different types of resources, such as trees that you can cut down, and ores that you can mine.
I want the ores to spawn more commonly on mountains, but still spawning on regular ground.
I also want to have different biomes, like in minecraft.
I currently have a LocalScript in StarterCharacterScripts, which interacts with a ModuleScript I have in ReplicatedStorage, which generates and loads chunks, and gets rid of chunks that are a certain distance away from the player, also like minecraft’s render distance.
I also have a Script in ServerScriptService, which randomly generates a random value from 1 to 1 million, in an IntValue called Seed. This is a world seed function, like minecraft’s world seeds.
Finally, the ModuleScript is the script that generates the world inside the chunks, adds the resources, and other things.
Here is the code for the LocalScript:
local Chunk = require(game.ReplicatedStorage.Chunk)
local RENDER_DISTANCE = 12
local CHUNKS_LOADED_PER_TICK = 4
local CAMERA = workspace.CurrentCamera
local centerPosX
local centerPosZ
local chunks = {}
local chunkCount = 0
local fastLoad = true
local function chunkWait()
chunkCount = (chunkCount + 1) % CHUNKS_LOADED_PER_TICK
if chunkCount == 0 and not fastLoad then
wait()
end
end
local function updateCenterPosFromCamera()
local camPos = CAMERA.CFrame.Position
centerPosX = math.floor(camPos.X / Chunk.WIDTH_SIZE_X)
centerPosZ = math.floor(camPos.Z / Chunk.WIDTH_SIZE_Z)
end
local function doesChunkExist(x, z)
for index, chunk in pairs(chunks) do
if chunk.x == x and chunk.z == z then
return true
end
end
return false
end
local function isChunkOutOfRange(chunk)
if math.abs(chunk.x - centerPosX) > RENDER_DISTANCE
or math.abs(chunk.z - centerPosZ) > RENDER_DISTANCE then
return true
end
return false
end
local function makeChunks()
for x = centerPosX - RENDER_DISTANCE, centerPosX + RENDER_DISTANCE do
for z = centerPosZ - RENDER_DISTANCE, centerPosZ + RENDER_DISTANCE do
if not doesChunkExist(x, z) then
table.insert(chunks, Chunk.new(x, z))
chunkWait()
end
end
end
end
local function destroyChunks()
local n = #chunks
for i = 1, n do
local chunk = chunks[i]
if isChunkOutOfRange(chunk) then
chunk:Destroy()
chunkWait()
chunks[i] = nil
end
end
local j = 0
for i = 1, n do
if chunks[i] ~= nil then
j += 1
chunks[j] = chunks[i]
end
end
for i = j + 1, n do
chunks[i] = nil
end
end
while true do
updateCenterPosFromCamera()
destroyChunks()
makeChunks()
fastLoad = false
wait(0.5)
end
Here is the code for the ModuleScript:
local TERRAIN_HEIGHT_COLORS = {
[-50] = Color3.fromRGB(216, 204, 157); -- sand yellow
[-10] = Color3.fromRGB(72, 113, 58); -- grassy green
[0] = Color3.fromRGB(72, 113, 58); -- grassy green
[75] = Color3.fromRGB(76, 80, 86); -- stone grey mountain
}
local X, Z = 4, 4
local WIDTH_SCALE = 15
local HEIGHT_SCALE = 100
local TERRAIN_SMOOTHNESS = 20
local MIN_TREE_SPAWN_HEIGHT = -15
local MAX_TREE_SPAWN_HEIGHT = 30
local TREE_DENSITY = 0.35
local Seed = workspace.Seed.Value
local wedge = Instance.new("WedgePart");
wedge.Anchored = true;
wedge.TopSurface = Enum.SurfaceType.Smooth;
wedge.BottomSurface = Enum.SurfaceType.Smooth;
local function draw3dTriangle(a, b, c)
local ab, ac, bc = b - a, c - a, c - b;
local abd, acd, bcd = ab:Dot(ab), ac:Dot(ac), bc:Dot(bc);
if (abd > acd and abd > bcd) then
c, a = a, c;
elseif (acd > bcd and acd > abd) then
a, b = b, a;
end
ab, ac, bc = b - a, c - a, c - b;
local right = ac:Cross(ab).unit;
local up = bc:Cross(right).unit;
local back = bc.unit;
local height = math.abs(ab:Dot(up));
local w1 = wedge:Clone();
w1.Size = Vector3.new(0, height, math.abs(ab:Dot(back)));
w1.CFrame = CFrame.fromMatrix((a + b)/2, right, up, back);
w1.Parent = workspace;
local w2 = wedge:Clone();
w2.Size = Vector3.new(0, height, math.abs(ac:Dot(back)));
w2.CFrame = CFrame.fromMatrix((a + c)/2, -right, up, -back);
w2.Parent = workspace;
return w1, w2;
end
local function getHeight(chunkPosX, chunkPosZ, x, z)
local height = math.noise(
(X/TERRAIN_SMOOTHNESS * chunkPosX) + x/TERRAIN_SMOOTHNESS,
(Z/TERRAIN_SMOOTHNESS * chunkPosZ) + z/TERRAIN_SMOOTHNESS,
Seed
) * HEIGHT_SCALE
if height > 20 then
local difference = height - 20
height += (difference * 1.2)
end
if height < -20 then
local difference = height - -20
height += (difference * 1.2)
end
return height
end
local function getPosition(chunkPosX, chunkPosZ, x, z)
return Vector3.new(
chunkPosX*X*WIDTH_SCALE + x*WIDTH_SCALE,
getHeight(chunkPosX, chunkPosZ, x, z),
chunkPosZ*Z*WIDTH_SCALE + z*WIDTH_SCALE
)
end
local function paintWedge(wedge)
local wedgeHeight = wedge.Position.Y
local color
local lowerColorHeight
local higherColorHeight
for height, heightColor in pairs(TERRAIN_HEIGHT_COLORS) do
if wedgeHeight == height then
color = heightColor
break
end
if (wedgeHeight < height) and (not higherColorHeight or height < higherColorHeight) then
higherColorHeight = height
end
if (wedgeHeight > height) and (not lowerColorHeight or height > lowerColorHeight) then
lowerColorHeight = height
end
end
if not color then
if higherColorHeight == nil then
color = TERRAIN_HEIGHT_COLORS[lowerColorHeight]
elseif lowerColorHeight == nil then
color = TERRAIN_HEIGHT_COLORS[higherColorHeight]
else
local alpha = (wedgeHeight - lowerColorHeight) / (higherColorHeight - lowerColorHeight)
local lowerColor = TERRAIN_HEIGHT_COLORS[lowerColorHeight]
local higherColor = TERRAIN_HEIGHT_COLORS[higherColorHeight]
color = lowerColor:lerp(higherColor, alpha)
end
end
wedge.Material = Enum.Material.Grass
wedge.Color = color
end
local function addWater(chunk)
local cframe = CFrame.new(
(chunk.x + .5) * chunk.WIDTH_SIZE_X,
-70,
(chunk.z + .5) * chunk.WIDTH_SIZE_Z
)
local size = Vector3.new(
chunk.WIDTH_SIZE_X,
90,
chunk.WIDTH_SIZE_Z
)
workspace.Terrain:FillBlock(cframe, size, Enum.Material.Water)
chunk.waterCFrame = cframe
chunk.waterSize = size
end
local function addTrees(chunk)
local posGrid = chunk.positionGrid
local instances = chunk.instances
local chunkPosX = chunk.x
local chunkPosZ = chunk.z
for x = 0, X-1 do
for z = 0, Z-1 do
local pos = posGrid[x][z]
if pos.Y >= MIN_TREE_SPAWN_HEIGHT and pos.Y <= MAX_TREE_SPAWN_HEIGHT then
math.randomseed(x * (chunkPosX+Seed) + z * (chunkPosZ+Seed))
if math.random() < TREE_DENSITY then
local tree = game.ReplicatedStorage.Tree:Clone()
for index, child in pairs(tree:GetChildren()) do
if child.Name == "Leaf" then
child.Color = Color3.fromRGB(
75 + math.random(-25, 25),
151 + math.random(-25, 25),
75 + math.random(-25, 25)
)
end
end
local cframe = CFrame.new(pos)
* CFrame.new(
math.random() * math.random(-10, 10),
0,
math.random() * math.random(-10, 10)
)
* CFrame.Angles(0, 2 * math.pi * math.random(), 0)
tree:SetPrimaryPartCFrame(cframe)
tree.Parent = workspace
table.insert(instances, tree)
end
end
end
end
end
local Chunk = {}
Chunk.__index = Chunk
Chunk.WIDTH_SIZE_X = X * WIDTH_SCALE
Chunk.WIDTH_SIZE_Z = Z * WIDTH_SCALE
function Chunk.new(chunkPosX, chunkPosZ)
local chunk = {
instances = {};
positionGrid = {};
x = chunkPosX;
z = chunkPosZ;
}
setmetatable(chunk, Chunk)
local positionGrid = chunk.positionGrid
for x = 0, X do
positionGrid[x] = {}
for z = 0, Z do
positionGrid[x][z] = getPosition(chunkPosX, chunkPosZ, x, z)
end
end
for x = 0, X-1 do
for z = 0, Z-1 do
local a = positionGrid[x][z]
local b = positionGrid[x+1][z]
local c = positionGrid[x][z+1]
local d = positionGrid[x+1][z+1]
local wedgeA, wedgeB = draw3dTriangle(a, b, c)
local wedgeC, wedgeD = draw3dTriangle(b, c, d)
paintWedge(wedgeA)
paintWedge(wedgeB)
paintWedge(wedgeC)
paintWedge(wedgeD)
table.insert(chunk.instances, wedgeA)
table.insert(chunk.instances, wedgeB)
table.insert(chunk.instances, wedgeC)
table.insert(chunk.instances, wedgeD)
end
end
addWater(chunk)
addTrees(chunk)
return chunk
end
function Chunk:Destroy()
for index, instance in ipairs(self.instances) do
instance:Destroy()
end
workspace.Terrain:FillBlock(self.waterCFrame, self.waterSize, Enum.Material.Air)
end
return Chunk
and here is the code for the seed Script:
workspace.Seed.Value = math.random(1000000)
Any ideas on how to add onto this?