Help with game optimization script?

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 :slight_smile:

2 Likes

That script isn’t the one spawning in things unless I am really blind when I looked over it three times just now?

Sorry, you are right. I have added the block placement code about the optimization code in the original post.

1 Like
		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

I have a feeling this part of the code is running a lot more times than it should. Have you tried putting print(1), print(2), etc, everywhere in your code to see everything that is firing or if something is firing multiple times when it shouldnt?

Thanks, I took your idea and was able to create this to check that the chunk hadn’t already been loaded by the script:

NewPos = Vector3.new(CutToGrid(NewPos.X), CutToGrid(NewPos.Y), CutToGrid(NewPos.Z))
			
if BlocksFolder:FindFirstChild(tostring(NewPos)) then -- Checks for starting block in folder
else
	LoadChunk(NewPos) -- Loads the chunk from the given coordinate
end

Thank you, this definitely helped the game’s performance; however, the same issue still seems to occur after loading larger amounts of blocks.

Thanks again :slight_smile:

1 Like

Glad I could help! Also about this:

Possibly because you havent unloaded the chunk fully? I’m not sure about this but try checking the part when it gets unloaded.

The lag may also be because this is all being handled client sided? Can you not update this server-sided?

I do also have a completely different solution but would require you to change up a few things, however. Instead of spawning and deleting parts, what about just spawning them in when the game loads, but just make them invisible, turn cancollide off, and turn shadows off? Also turning collision and shadows off for the 2nd part beneath you (you will never touch it nor see it)?