Hello, when developing a project, I have experienced some issues when attempting to optimize map generation/loading; however, I have struggled to create a system which does not cause much or any lag on a device.
The map is made of blocks similar to other simple sandbox games
Here is a video of the issue:
The lag seems to mostly occur when registering new parts.
This is a large issue as the device I am playing on is a someone decent PC so the game is not easily playable on other devices.
Here is the code to load the blocks:
local ChunkSize = 3 -- Size of chunks
-- Preset data
local BlocksFolder = game.Workspace:WaitForChild("Blocks")
local WorldValues = game.ReplicatedStorage:WaitForChild("WorldValues")
local GridSize = WorldValues:WaitForChild("GridSize").Value
local MaxDist = WorldValues:WaitForChild("MaxRenderDistance").Value
local function CutToGrid(Num) -- Rounds a number to the grid size
return math.round(Num/GridSize)*GridSize
end
-- Changes made to the map
local Alters = game.ReplicatedStorage:WaitForChild("AlteredBlocks")
local Removed = Alters:WaitForChild("Removed")
-- The seed for the map
local Seed = game.ReplicatedStorage:WaitForChild("WorldValues"):WaitForChild("Seed").Value
local function GetHeight(X,Z) -- Calulates height using noise (not final)
local Y = 0
local Zoom = 100
local Basic = math.noise(X/Zoom, Z/Zoom, Seed)*10
Y = Basic
return Y
end
local function ApplyBlockType(Block : BasePart, Name : string)
if Name then
local Colors = { -- Colors of blocks
["Grass"] = Color3.fromRGB(34, 175, 26);
["Dirt"] = Color3.fromRGB(136, 88, 33);
["Rock"] = Color3.fromRGB(152, 170, 175);
}
if Colors[Name] then -- Checks for an associated color
Block.Color = Colors[Name]
else
Block.Color = Color3.fromRGB(152, 28, 142)
end
else
Block.Color = Color3.fromRGB(152, 28, 142)
end
end
local Index = 0
local function AddBlock(Position : Vector3, Type : string) -- Adds a block
if BlocksFolder:FindFirstChild(tostring(Position)) then -- Checks if the position is already occupied
else
if Removed:FindFirstChild(tostring(Position)) then -- Checks if the block was removed
else
Index += 1
local Cube = game.ReplicatedStorage:WaitForChild("Cube"):Clone()
Cube.Parent = BlocksFolder
Cube.Name = tostring(Position)
Cube.Position = Position
ApplyBlockType(Cube, Type) -- Applies the specified block type
if Index/10 == math.round(Index/10) then -- Adds a delay every 10 blocks
task.wait()
task.wait()
end
end
end
end
local Depth = 2 -- Layers of dirt below the surface
local function LoadChunk(Position) -- Loads the chunk
for x=1, ChunkSize do
for z=1, ChunkSize do
-- Gets block position
local Pos = Position-Vector3.new(x*GridSize, 0, z*GridSize) + Vector3.new(GridSize*ChunkSize/2, 0, GridSize*ChunkSize/2)
Pos = Vector3.new(CutToGrid(Pos.X), 0, CutToGrid(Pos.Z))
Pos = Vector3.new(Pos.X, CutToGrid(GetHeight(Pos.X, Pos.Z))+(GridSize*10), Pos.Z) -- Aplies height from Noise values
-- Adds blocks
if Pos.Y > -1 then -- Ensures it is not off the map
AddBlock(Pos, "Grass") -- Adds ground blocks
end
for i=1, Depth do -- Adds dirt below grass
local PosB = Pos-Vector3.new(0, GridSize*i, 0)
if Pos.Y > -1 then -- Ensures it is not off the map
AddBlock(PosB, "Dirt") -- Adds dirt blocks
end
end
end
end
end
while task.wait() do -- Loops forever
if game.Players.LocalPlayer.Character then -- Makes sure the character exists
local Position = game.Players.LocalPlayer.Character.PrimaryPart.Position
local NewPos = Vector3.new(Position.X, 2, Position.Z)
NewPos = Vector3.new(CutToGrid(NewPos.X), CutToGrid(NewPos.Y), CutToGrid(NewPos.Z)) -- Character's position on grid
LoadChunk(Position) -- Loads the chunk the player is in
local Offsets = { -- Surrounding chunk coordinates
Vector3.new(0, 0, 1);
Vector3.new(0, 0, -1);
Vector3.new(1, 0, 1);
Vector3.new(1, 0, -1);
Vector3.new(-1, 0, 1);
Vector3.new(-1, 0, -1);
Vector3.new(1, 0, 0);
Vector3.new(-1, 0, 0);
}
for i, Offset in pairs(Offsets) do -- Loops through coordinates of surrounding chunks
local NewOff = Vector3.new(Offset.X*ChunkSize*GridSize, 0, Offset.Z*ChunkSize*GridSize)
local NewPos = Position - NewOff
LoadChunk(NewPos) -- Loads the chunk from the given coordinate
end
end
end
Removed.ChildAdded:Connect(function(Obj : Instance) -- Updates when a block is broken
if BlocksFolder:FindFirstChild(Obj.Name) then -- Checks if block is loaded for client
BlocksFolder:FindFirstChild(Obj.Name):Destroy() -- Destroys
print("Removed")
end
end)
Here is the code I am currently using to try and optimize the blocks:
-- Folder of blocks in workspace
local Folder = game.Workspace:WaitForChild("Blocks")
-- Predefined properties
local WorldValues = game.ReplicatedStorage:WaitForChild("WorldValues")
local GridSize = WorldValues:WaitForChild("GridSize").Value
local HeightLimit = WorldValues:WaitForChild("HeightLimit").Value
local MaxDist = WorldValues:WaitForChild("MaxRenderDistance").Value
local Plr = game.Players.LocalPlayer -- The player
Folder.ChildAdded:Connect(function(Obj) -- Registers a block when it is added to the folder in workspace
if Obj:IsA("BasePart") then
local function CheckDistance() -- Destroys the part if it is too far from the player
if Obj and Plr.Character then
local Pos = Obj.Position
local PosB = Plr.Character.PrimaryPart.Position
local Dist = (Pos-PosB).Magnitude
if Dist < MaxDist then
else
if Obj and Obj.Parent then
Obj:Destroy()
end
end
end
end
local function AttemptCheck(NewItem : BasePart) -- Checks if the part is surrounded by other parts
if Obj then
local Offsets = {
Vector3.new(0, GridSize, 0); -- Top
Vector3.new(0, -GridSize, 0); -- Bottom
Vector3.new(GridSize, 0, 0); -- Left
Vector3.new(-GridSize, 0, 0); -- Right
Vector3.new(0, 0, GridSize); -- Front
Vector3.new(0, 0, -GridSize); -- Back
}
local Hidden = 1
for i, Offset in pairs(Offsets) do -- Loops through all possible surrounding parts
local Offseted = tostring(Offset+Obj.Position) -- Calculates position and converts to string
if Folder:FindFirstChild(Offseted) then -- Checks for part in folder
else
Hidden = 0
break
end
end
if Hidden == 1 then -- Checks if the part should be hidden
Obj.CastShadow = false
Obj.Transparency = 1
Obj.CanCollide = false
else
Obj.CastShadow = true
Obj.Transparency = 0
Obj.CanCollide = true
end
end
end
local function CheckIfRelevant(Other : BasePart) -- Checks if the new part is close to the current part
CheckDistance() -- Checks if the current part needs to be destroyed
task.wait()
if Obj then -- Checks if it was destroyed
local PosX = Other.Position
local Dist = (PosX-Obj.Position).Magnitude -- Calculates distance
if Dist <= GridSize*3 then -- Checks if it is nearby
task.wait()
AttemptCheck(Obj) -- Checks the part
end
end
end
task.wait()
Folder.ChildAdded:Connect(function(Cube) -- Detects a new part being added
if Obj and Cube and Cube:IsA("BasePart") then
CheckIfRelevant(Cube)
end
end)
end
end)
I think the lag is being caused by the optimization.
If you have any ideas on how to optimize this, please reply and let me know.
Game link if you want it:
Block World - Test Place
Thanks for reading!
Have a good day