Need help generating different types of resources for a multiplayer survival game

(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?

1 Like

it wasn’t getting any responses, i thought it was just dead.

i might just flag it myself and delete it…

I think you could loop the chunk you want the tree to generate, get the blocks that have one side facing an empty air block and check if the air block’s Y is greater than the default Y for the biome (more onto that later), if it is and the biome is ‘Mountains’ then just call a function that generates coal more times.

I suggest reading this Biome – Minecraft Wiki article about Minecraft biomes and maybe watching some videos about how chunks generate in Minecraft, here is a video that may help: How Do Minecraft Worlds Generate?! - YouTube (How Do Minecraft Worlds Generate?! by AntVenom)

You can obviously read a lot on the internet about generating world in minecraft.

I am sorry that I can’t help any further since I don’t really work with world generations or such things, this is only my suggestion that may help you.

1 Like

I already have a chunk generating system in place here, but I don’t know how to add more resources, because I already have trees spawning.

Note: Currently you cannot interact with the trees at all.

Edit: Here is how the world currently looks:

1 Like

You can just use the function you already created for the tree generation, you just need to change few things, for example MIN_TREE_SPAWN_HEIGHT and MAX_TREE_SPAWN_HEIGHT as well as the ore you are spawning. You can add a value that will set the density of coal, for example

local coalDensity = math.random(3,7) * (pos.Y * MAX_COAL_SPAWN_HEIGHT).

I don’t really know what some things are, for example if math.random() < TREE_DENSITY then ... so I can’t really help you further.

I could do that, but by the way you cannot cut down trees and stuff right now.

I copied the code, changed it a bit, and put it in the same script, and they did not spawn. Huh.