Hi devs, I have had alot of problems with my chunk generation that I have made yesterday and I am calling out for help from you all.
- What do you want to achieve? Keep it simple and clear!
- I want to achieve a performance boost in my chunk generation.
- What is the issue? Include screenshots / videos if possible!
- No screenshots because there’s no need
- What solutions have you tried so far? Did you look for solutions on the Developer Hub?
- Yes but they didn’t contain produceral generation, only chunks of buildings and stuff.
- I also have tried parallel lua but nothing changed maybe there’s something in my script that causes the game to lag even with parallel lua
These are my scripts so far.
MODULE (not the entire thing):
local GeneratedTreePositions = {}
local DestroyedBlocks = {}
local NoiseGenerator = {
CHUNK_SIZE = CHUNK_SIZE,
BLOCK_SIZE = BLOCK_SIZE
}
function NoiseGenerator.BlockCulling(block)
local Directions = {
TOP = {name = "TOP"; dir = block.CFrame.UpVector * 3},
BOTTOM = {name = "BOTTOM"; dir = block.CFrame.UpVector * -3},
LEFT = {name = "LEFT"; dir = block.CFrame.RightVector * -3},
RIGHT = {name = "RIGHT"; dir = block.CFrame.RightVector * 3},
FRONT = {name = "FRONT"; dir = block.CFrame.LookVector * 3},
BACK = {name = "BACK"; dir = block.CFrame.LookVector * -3},
}
local function destroyIfRaycastHit(direction)
local raycastResult = workspace:Raycast(block.Position, direction.dir)
if raycastResult then
local part = block:FindFirstChild(direction.name)
if part then
part:Destroy()
end
end
end
for _, direction in pairs(Directions) do
destroyIfRaycastHit(direction)
end
local blockCulled = Instance.new("BoolValue")
blockCulled.Name = "IsCulled"
blockCulled.Parent = block
end
function NoiseGenerator.GenerateChunk(chunkX, chunkZ, heartbeat)
local chunkFolder = Instance.new("Folder")
chunkFolder.Parent = workspace.Map
chunkFolder.Name = "Chunk[" .. chunkX .. "-" .. chunkZ .. "]"
local random = Random.new(SEED + chunkX * 31 + chunkZ * 17)
NoiseGenerator.RemoveDestroyedBlock()
for x = 1, CHUNK_SIZE do
for z = 1, CHUNK_SIZE do
local realX = (chunkX - 1) * CHUNK_SIZE + x
local realZ = (chunkZ - 1) * CHUNK_SIZE + z
local noise = math.noise(SEED, realX / 12, realZ / 12) * AMPLITUDE
local blockPosition = Vector3.new(realX, math.floor(noise), realZ)
local block = game.ReplicatedStorage.Items["Grass Block"]:Clone()
workspace.DescendantRemoving:Connect(function(descendant)
if descendant == block and not NoiseGenerator.IsBlockDestroyed(block) then
NoiseGenerator.AddDestroyedBlock(block)
end
end)
if block then
block.Parent = chunkFolder
block.Position = Vector3.new(realX * BLOCK_SIZE, math.floor(noise) * BLOCK_SIZE, realZ * BLOCK_SIZE)
-- Place trees based on noise value and deterministic random
if random:NextNumber() > 0.995 then
local treeStem = game.ReplicatedStorage.Items.Trees:GetChildren()[random:NextInteger(1, #game.ReplicatedStorage.Items.Trees:GetChildren())]:Clone() -- Clone a random tree from ReplicatedStorage
local treePosition = Vector3.new(realX * BLOCK_SIZE, math.floor(noise) * BLOCK_SIZE + BLOCK_SIZE, realZ * BLOCK_SIZE) -- Calculate the position of the tree
treeStem:PivotTo(CFrame.new(treePosition)) -- Set the position of the tree
treeStem.Parent = chunkFolder -- Set the parent of the tree to the chunk folder
local Parameters = RaycastParams.new() -- Create a new RaycastParams object
Parameters.FilterType = Enum.RaycastFilterType.Exclude -- Set the filter type to Exclude
Parameters.FilterDescendantsInstances = {treeStem} -- Exclude the tree itself from raycast
local HitRaycast = workspace:Raycast(treeStem:GetPivot().Position, Vector3.new(0, -5, 0), Parameters) -- Perform a raycast to check for ground
if HitRaycast then
treeStem:PivotTo(CFrame.new(HitRaycast.Position)) -- Set the position of the tree to the ground
end
end
-- Place trees based on noise value and deterministic random
if random:NextNumber() > 0.8 then
local grass = game.ReplicatedStorage.Items.Grass:Clone() -- Clone a random tree from ReplicatedStorage
local grassPosition = Vector3.new(realX * BLOCK_SIZE, math.floor(noise) * BLOCK_SIZE + BLOCK_SIZE, realZ * BLOCK_SIZE) -- Calculate the position of the tree
grass:PivotTo(CFrame.new(grassPosition)) -- Set the position of the tree
grass.Parent = chunkFolder -- Set the parent of the tree to the chunk folder
local Parameters = RaycastParams.new() -- Create a new RaycastParams object
Parameters.FilterType = Enum.RaycastFilterType.Exclude -- Set the filter type to Exclude
Parameters.FilterDescendantsInstances = {grass} -- Exclude the tree itself from raycast
local HitRaycast = workspace:Raycast(grass:GetPivot().Position, Vector3.new(0, -5, 0), Parameters) -- Perform a raycast to check for ground
if HitRaycast then
grass:PivotTo(CFrame.new(HitRaycast.Position)) -- Set the position of the tree to the ground
end
end
end
if heartbeat then
coroutine.yield()
end
end
end
for _, v in pairs(chunkFolder:GetChildren()) do
if v.Name == "Grass Block" then
if not v:FindFirstChild("IsCulled") then
NoiseGenerator.BlockCulling(v)
end
end
end
return chunkFolder
end
return NoiseGenerator
CLIENT:
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local rootPart = character:WaitForChild("HumanoidRootPart")
local chunkRadius = 2 -- Number of chunks to load around the player
local unloadDistance = chunkRadius -- Distance to unload chunks
local NoiseGenerator = require(game.ReplicatedStorage.Modules:WaitForChild("NoiseGenerator"))
local loadedChunks = {}
local function getChunkCoords(position)
local chunkX = math.floor(position.X / (NoiseGenerator.CHUNK_SIZE * NoiseGenerator.BLOCK_SIZE))
local chunkZ = math.floor(position.Z / (NoiseGenerator.CHUNK_SIZE * NoiseGenerator.BLOCK_SIZE))
return chunkX, chunkZ
end
-- Coroutine to load a chunk
local function loadChunkAsync(chunkX, chunkZ, Heartbeat)
if not loadedChunks[chunkX] then
loadedChunks[chunkX] = {}
end
if not loadedChunks[chunkX][chunkZ] then
loadedChunks[chunkX][chunkZ] = NoiseGenerator.GenerateChunk(chunkX, chunkZ, Heartbeat)
end
end
-- Coroutine to unload a chunk
local function unloadChunkAsync(chunkX, chunkZ)
if loadedChunks[chunkX] and loadedChunks[chunkX][chunkZ] then
loadedChunks[chunkX][chunkZ]:Destroy()
loadedChunks[chunkX][chunkZ] = nil
end
end
local function updateChunks()
local currentChunkX, currentChunkZ = getChunkCoords(rootPart.Position)
for chunkX, chunks in pairs(loadedChunks) do
for chunkZ, _ in pairs(chunks) do
if math.abs(chunkX - currentChunkX) > unloadDistance or math.abs(chunkZ - currentChunkZ) > unloadDistance then
unloadChunkAsync(chunkX, chunkZ)
end
end
end
for x = -chunkRadius, chunkRadius do
for z = -chunkRadius, chunkRadius do
local chunkX = currentChunkX + x
local chunkZ = currentChunkZ + z
loadChunkAsync(chunkX, chunkZ, false)
end
end
end
game:GetService("RunService").Stepped:Connect(updateChunks)
-- Initial load
updateChunks()
I really need help.