Lag with building system

  1. What do you want to achieve?

I want to create a building system that allows users to place objects on their plot.

  1. What is the issue?

After placing a few items (20 - 30) the game starts to lag, there are no unanchored parts. Not all parts of the game lag, ones I’ve noticed so far are weapon cooldowns increasing, physics lagging and when deleting items there is a delay too.

  1. What solutions have you tried so far?
    I’ve tried messing around with Network Ownership (fairly new to this) and tried setting the ownership to the server. Although since none of the objects are unanchored this does not work.

Here is the code for the building system when placing an items
Client -

 mouse.Button1Up:Connect(function()
							if placingObject then
								if player.leaderstats.Cash.Value >= objFolder[buttonFrame.Name]:GetAttribute("Price") then
									placingObject = false

									game.SoundService.HammerAndNail:Play()

									for _, sideButton in pairs(player.PlayerGui.ScreenGui.MainButtons:GetChildren()) do
										sideButton.Visible = true
									end
									for _, sideButton in pairs(player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren()) do
										sideButton.Visible = false
									end

									placementEvent:FireServer(previewObject.Name, previewObject.PrimaryPart.CFrame)
									client.OpenUI(player.PlayerGui.ScreenGui.Building)
									DisplayStructures()
									previewObject:Destroy()
								else
									client.OpenUI(player.PlayerGui.ScreenGui.Building)
									DisplayStructures()
									previewObject:Destroy()
									placingObject = false

									for _, sideButton in pairs(player.PlayerGui.ScreenGui.MainButtons:GetChildren()) do
										sideButton.Visible = true
									end
									for _, sideButton in pairs(player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren()) do
										sideButton.Visible = false
									end
								end
							end
						end)

Server -

game.ReplicatedStorage.RemoteEvents.PlacementEvent.OnServerEvent:Connect(function(player, previewObject, objCframe)
	local objFolder = game.ReplicatedStorage:WaitForChild("ObjectFolder")
	local object = objFolder:FindFirstChild(previewObject):Clone()
	local price = object:GetAttribute("Price")

	player.leaderstats.Cash.Value -= price

	object:SetPrimaryPartCFrame(objCframe)
	
	for _, model in pairs(object:GetChildren()) do
		if model:IsA("Model") then
			model.Parent = workspace.Plots[player.Name].Structures
			object:Destroy()
			
			local moneyUI = model.MoneyGain
			moneyUI.TextLabel.TextTransparency = 1
			moneyUI.TextLabel.UIStroke.Transparency = 1
			moneyUI.StudsOffset = Vector3.new(0,0,0)
			moneyUI.Enabled = true
			moneyUI.TextLabel.Text = "-$" .. tostring(price)
			game:GetService("TweenService"):Create(moneyUI.TextLabel, TweenInfo.new(0.3), {TextTransparency = 0}):Play()
			game:GetService("TweenService"):Create(moneyUI.TextLabel.UIStroke, TweenInfo.new(0.3), {Transparency = 0}):Play()

			game:GetService("TweenService"):Create(moneyUI, TweenInfo.new(1), {StudsOffset = Vector3.new(0, 3, 0)}):Play()
			wait(0.8)
			game:GetService("TweenService"):Create(moneyUI.TextLabel, TweenInfo.new(0.3), {TextTransparency = 1}):Play()
			game:GetService("TweenService"):Create(moneyUI.TextLabel.UIStroke, TweenInfo.new(0.3), {Transparency = 1}):Play()
			wait(0.3)
			moneyUI.Enabled = false
		end
	end
end)
1 Like

Have you tried checking the server stats from the developer console while you are building? You should also check it out with the script profiler to make sure that it is because of this script.

Other than that, you should do the GUI animation at client side, that is more ideal. Your issue is probably not there though, yielding (the wait() here counts as well) is not usually advised in functions bound to events.

I’ve never really used the developer console to view stats, can you specify what I should specifically be looking out for that could give me leads on the lag?

In ServerJobs you can see all of the stats. Anything (might depend on some settings) much lower than 60 has a potential of causing these lags, mostly check the Heartbeat one.

1 Like


Almost all seem to be under 60. The HeartBeat one averages around 40 - 50.

The values are really bad and lag is expected. Now can you use the script profiler to see which script has the most activity?


Not sure what this is telling me or what I should be looking for

If you click on these it will show you more info.

I’ll just make an assumption and suggest you to replace your server script with this:

game.ReplicatedStorage.RemoteEvents.PlacementEvent.OnServerEvent:Connect(function(player, previewObject, objCframe)
	local objFolder = game.ReplicatedStorage:WaitForChild("ObjectFolder")
	local object = objFolder:FindFirstChild(previewObject):Clone()
	local price = object:GetAttribute("Price")

	player.leaderstats.Cash.Value -= price

	object:SetPrimaryPartCFrame(objCframe)
	
	for _, model in pairs(object:GetChildren()) do
		if model:IsA("Model") then
			model.Parent = workspace.Plots[player.Name].Structures
			object:Destroy()
			
			local moneyUI = model.MoneyGain
			moneyUI.TextLabel.TextTransparency = 1
			moneyUI.TextLabel.UIStroke.Transparency = 1
			moneyUI.StudsOffset = Vector3.new(0,0,0)
			moneyUI.Enabled = true
			moneyUI.TextLabel.Text = "-$" .. tostring(price)

			task.spawn(function()
				game:GetService("TweenService"):Create(moneyUI.TextLabel, TweenInfo.new(0.3), {TextTransparency = 0}):Play()
				game:GetService("TweenService"):Create(moneyUI.TextLabel.UIStroke, TweenInfo.new(0.3), {Transparency = 0}):Play()

				game:GetService("TweenService"):Create(moneyUI, TweenInfo.new(1), {StudsOffset = Vector3.new(0, 3, 0)}):Play()
				task.wait(0.8)
				game:GetService("TweenService"):Create(moneyUI.TextLabel, TweenInfo.new(0.3), {TextTransparency = 1}):Play()
				game:GetService("TweenService"):Create(moneyUI.TextLabel.UIStroke, TweenInfo.new(0.3), {Transparency = 1}):Play()
				task.wait(0.3)
				moneyUI.Enabled = false
			end)
		end
	end
end)

Tried replacing the server code with yours, didn’t seem to fix much.


Heres what the WaitingScripts looks like
‘Building’ is the Local Script that handles the building.

There are a lot of missing segments from the local script you shared here. It’s probably related with something else in that script.

Also yes that was expected to not matter a lot but you should still do that UI animation on client as an improvement.

I didn’t see that you selected Client on ScriptProfiler, that was of course not going to change anything. My bad.

1 Like

It’s all good, Here is the rest of the Building script, apologizes for leaving it out.

This is my first time making a building system so the code is a bit messy, also I’ve never really done research on performance so I appreciate you guided me through this.

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local client = require(game.StarterPlayer.StarterPlayerScripts:WaitForChild("Client"))
local ViewportModel = require(game.ReplicatedStorage.ViewportModel)

local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()

local placementEvent = game.ReplicatedStorage.RemoteEvents.PlacementEvent
local objFolder = game.ReplicatedStorage:WaitForChild("ObjectFolder")

local mouse = player:GetMouse()

local Frame = player.PlayerGui.ScreenGui.Building.ScrollingFrame
local itemPopup = player.PlayerGui.ScreenGui.BuildingItemPopup

local placingObject = false
local rotatingObject = false

local function DisplayStructures()
	for _, v in pairs(game.Workspace.Plots[player.Name].Structures:GetChildren()) do
		local highlight = Instance.new("Highlight", v)
		highlight.OutlineColor = Color3.new(0.741176, 0.741176, 0.741176)
		highlight.FillColor = Color3.new(1, 1, 1)
		
		spawn(function()
			while wait() do
				wait(0.5)
				game:GetService("TweenService"):Create(highlight, TweenInfo.new(0.5), {FillColor = Color3.new(1, 1, 1)}):Play()
				wait(0.5)
				game:GetService("TweenService"):Create(highlight, TweenInfo.new(0.5), {FillColor = Color3.new(0.784314, 0.784314, 0.784314)}):Play()
			end
		end)
	end
end
local function DisableStructures()
	for _, v in pairs(game.Workspace.Plots[player.Name].Structures:GetDescendants()) do
		if v:IsA("Highlight") then
			v:Destroy()
		end
	end
end

local function isInsidePlot(position, plotPart)
	local plotSize = plotPart.Size / 2
	local plotCFrame = plotPart.CFrame

	local plotMin = plotCFrame.Position - plotSize
	local plotMax = plotCFrame.Position + plotSize

	return position.X >= plotMin.X and position.X <= plotMax.X
		and position.Z >= plotMin.Z and position.Z <= plotMax.Z
end

--Placement
local function EnablePlacement()
	for _, buttonFrame in pairs(Frame:GetChildren()) do
		if buttonFrame:IsA("Frame") then
			local button:ImageButton = buttonFrame.Button

			button.MouseButton1Click:Connect(function()
				itemPopup.Visible = true
				client.OpenUI(player.PlayerGui.ScreenGui.Building)
				DisableStructures()

				local object = game.ReplicatedStorage.ObjectFolder[buttonFrame.Name]

				itemPopup.ObjectName.Text = buttonFrame.Name
				itemPopup.Price.Text = buttonFrame.Cost.Text
				itemPopup.Desc.Text = object:GetAttribute("Desc")

				local camClone = object:Clone()
				local camera = Instance.new("Camera")
				camera.FieldOfView = 70
				camera.Parent = itemPopup.ViewportFrame

				camClone.Parent = itemPopup.ViewportFrame
				itemPopup.ViewportFrame.CurrentCamera = camera

				local vpfModel = ViewportModel.new(itemPopup.ViewportFrame, camera)
				local cf, size = camClone:GetBoundingBox()

				vpfModel:SetModel(camClone)

				local theta = 0
				local orientation = CFrame.new()
				local distance = vpfModel:GetFitDistance(cf.Position)

				game:GetService("RunService").RenderStepped:Connect(function(dt)
					theta = theta + math.rad(20 * dt)
					orientation = CFrame.fromEulerAnglesYXZ(math.rad(-20), theta, 0)
					camera.CFrame = CFrame.new(cf.Position) * orientation * CFrame.new(0, 0, distance)
				end)

				itemPopup.Place.MouseButton1Click:Connect(function()
					camClone:Destroy()
					camera:Destroy()

					itemPopup.Visible = false

					if not placingObject then
						for _, sideButton in pairs(player.PlayerGui.ScreenGui.MainButtons:GetChildren()) do
							sideButton.Visible = false
						end
						for _, sideButton in pairs(player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren()) do
							sideButton.Visible = true
						end

						placingObject = true

						local rotationAmount = 0
						local gridSnapping = 0.5

						local previewObject = objFolder:FindFirstChild(buttonFrame.Name):Clone()
						previewObject.Parent = workspace

						for _, part in pairs(previewObject:GetDescendants()) do
							if part:IsA("BasePart") then
								part.Transparency = 0.5
								part.CanCollide = false
							end
							if part:IsA("ProximityPrompt") then
								part.Enabled = false
							end
						end

						UserInputService.InputBegan:Connect(function(key, gp)
							if not gp then
								if key.KeyCode == Enum.KeyCode.R then
									rotatingObject = true
									rotationAmount += 90

									game.SoundService.GridSnap:Play()
								end

								if key.KeyCode == Enum.KeyCode.X then
									if placingObject then
										rotatingObject = false
										placingObject = false
										previewObject:Destroy()
										client.OpenUI(player.PlayerGui.ScreenGui.Building)
										DisplayStructures()
										game.SoundService.CancelPlacement:Play()

										for _, sideButton in pairs(player.PlayerGui.ScreenGui.MainButtons:GetChildren()) do
											sideButton.Visible = true
										end
										for _, sideButton in pairs(player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren()) do
											sideButton.Visible = false
										end
									end
								end
							end
						end)

						UserInputService.InputEnded:Connect(function(key, gp)
							if key.KeyCode == Enum.KeyCode.R then
								rotatingObject = false
							end
						end)

						RunService.RenderStepped:Connect(function(deltaTime)
							if placingObject then
								mouse.TargetFilter = previewObject

								if previewObject:FindFirstChild("MainPart") then
									local ObjectCFrame = CFrame.new(math.round(mouse.hit.Position.X/gridSnapping)*gridSnapping, mouse.hit.Position.Y + previewObject.PrimaryPart.Size.Y/2, math.round(mouse.hit.Position.Z/gridSnapping)*gridSnapping)
									local ObjectAngles = CFrame.Angles(0,math.rad(rotationAmount), 0)

									if isInsidePlot(mouse.Hit.Position, game.Workspace.Plots[player.Name].PlotPart) then
										previewObject:SetPrimaryPartCFrame(ObjectCFrame * ObjectAngles)
									end
								end
							end
						end)

						mouse.Button1Up:Connect(function()
							if placingObject then
								if player.leaderstats.Cash.Value >= objFolder[buttonFrame.Name]:GetAttribute("Price") then
									placingObject = false

									game.SoundService.HammerAndNail:Play()

									for _, sideButton in pairs(player.PlayerGui.ScreenGui.MainButtons:GetChildren()) do
										sideButton.Visible = true
									end
									for _, sideButton in pairs(player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren()) do
										sideButton.Visible = false
									end

									placementEvent:FireServer(previewObject.Name, previewObject.PrimaryPart.CFrame)
									client.OpenUI(player.PlayerGui.ScreenGui.Building)
									DisplayStructures()
									previewObject:Destroy()
								else
									client.OpenUI(player.PlayerGui.ScreenGui.Building)
									DisplayStructures()
									previewObject:Destroy()
									placingObject = false

									for _, sideButton in pairs(player.PlayerGui.ScreenGui.MainButtons:GetChildren()) do
										sideButton.Visible = true
									end
									for _, sideButton in pairs(player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren()) do
										sideButton.Visible = false
									end
								end
							end
						end)
					end
				end)

				itemPopup.Cancel.MouseButton1Click:Connect(function()
					client.OpenUI(player.PlayerGui.ScreenGui.Building)
					camClone:Destroy()
					camera:Destroy()

					itemPopup.Visible = false
					DisplayStructures()
				end)
			end)
		end
	end
end

for _, object in pairs(game.ReplicatedStorage.ObjectFolder:GetChildren()) do
	local uiButton = game.ReplicatedStorage.BuildingItem:Clone()
	uiButton.Parent = player.PlayerGui.ScreenGui.Building.ScrollingFrame
	uiButton.ItemName.Text = object.Name
	uiButton.Cost.Text = "$"..tostring(object:GetAttribute("Price"))
	
	uiButton.Name = object.Name
	
	local camClone = object:Clone()

	local camera = Instance.new("Camera")
	camera.FieldOfView = 70
	camera.Parent = uiButton.ViewportFrame

	camClone.Parent = uiButton.ViewportFrame
	uiButton.ViewportFrame.CurrentCamera = camera

	local vpfModel = ViewportModel.new(uiButton.ViewportFrame, camera)
	local cf, size = camClone:GetBoundingBox()

	vpfModel:SetModel(camClone)

	local theta = 0
	local orientation = CFrame.new()
	local distance = vpfModel:GetFitDistance(cf.Position)

	game:GetService("RunService").RenderStepped:Connect(function(dt)
		theta = theta + math.rad(20 * dt)
		orientation = CFrame.fromEulerAnglesYXZ(math.rad(-20), theta, 0)
		camera.CFrame = CFrame.new(cf.Position) * orientation * CFrame.new(0, 0, distance)
	end)
	
	EnablePlacement()
end

for _, button in pairs(player.PlayerGui.ScreenGui.Building.Frame:GetChildren()) do
	if button:IsA("TextButton") then
		--Display Buttons
		button.MouseButton1Click:Connect(function()
			game.SoundService.OpenUI:Play()
			for _, frame in pairs(player.PlayerGui.ScreenGui.Building.ScrollingFrame:GetChildren()) do
				if frame:IsA("Frame") then
					frame:Destroy()
				end
			end
			
			if button.Name ~= "All" then
				for _, object in pairs(game.ReplicatedStorage.ObjectFolder:GetChildren()) do
					if object:GetAttribute("Type") == button.Name then
						local uiButton = game.ReplicatedStorage.BuildingItem:Clone()
						uiButton.Parent = player.PlayerGui.ScreenGui.Building.ScrollingFrame
						uiButton.ItemName.Text = object.Name
						uiButton.Cost.Text = "$"..tostring(object:GetAttribute("Price"))

						uiButton.Name = object.Name

						local camClone = object:Clone()

						local camera = Instance.new("Camera")
						camera.FieldOfView = 70
						camera.Parent = uiButton.ViewportFrame

						camClone.Parent = uiButton.ViewportFrame
						uiButton.ViewportFrame.CurrentCamera = camera

						local vpfModel = ViewportModel.new(uiButton.ViewportFrame, camera)
						local cf, size = camClone:GetBoundingBox()

						vpfModel:SetModel(camClone)

						local theta = 0
						local orientation = CFrame.new()
						local distance = vpfModel:GetFitDistance(cf.Position)

						game:GetService("RunService").RenderStepped:Connect(function(dt)
							theta = theta + math.rad(20 * dt)
							orientation = CFrame.fromEulerAnglesYXZ(math.rad(-20), theta, 0)
							camera.CFrame = CFrame.new(cf.Position) * orientation * CFrame.new(0, 0, distance)
						end)
						EnablePlacement()
					end
				end
			else
				for _, object in pairs(game.ReplicatedStorage.ObjectFolder:GetChildren()) do
					local uiButton = game.ReplicatedStorage.BuildingItem:Clone()
					uiButton.Parent = player.PlayerGui.ScreenGui.Building.ScrollingFrame
					uiButton.ItemName.Text = object.Name
					uiButton.Cost.Text = "$"..tostring(object:GetAttribute("Price"))

					uiButton.Name = object.Name

					local camClone = object:Clone()

					local camera = Instance.new("Camera")
					camera.FieldOfView = 70
					camera.Parent = uiButton.ViewportFrame

					camClone.Parent = uiButton.ViewportFrame
					uiButton.ViewportFrame.CurrentCamera = camera

					local vpfModel = ViewportModel.new(uiButton.ViewportFrame, camera)
					local cf, size = camClone:GetBoundingBox()

					vpfModel:SetModel(camClone)

					local theta = 0
					local orientation = CFrame.new()
					local distance = vpfModel:GetFitDistance(cf.Position)

					game:GetService("RunService").RenderStepped:Connect(function(dt)
						theta = theta + math.rad(20 * dt)
						orientation = CFrame.fromEulerAnglesYXZ(math.rad(-20), theta, 0)
						camera.CFrame = CFrame.new(cf.Position) * orientation * CFrame.new(0, 0, distance)
					end)
					
					EnablePlacement()
				end
			end
		end)
	end
end

-- Delete on MouseButton1Up
local selected
mouse.Button1Up:Connect(function()
	if player.PlayerGui.ScreenGui.Building.Visible and mouse.Target:FindFirstAncestor("Structures"):FindFirstAncestor(player.Name) and not selected then
		client.OpenUI(player.PlayerGui.ScreenGui.DeleteConfirmation)
		selected = mouse.Target.Parent 
		game.SoundService.OpenUI:Play()
		
		player.PlayerGui.ScreenGui.DeleteConfirmation.Yes.MouseButton1Click:Connect(function()
			client.OpenUI(player.PlayerGui.ScreenGui.DeleteConfirmation)
			selected:Destroy()
			game.SoundService.CancelPlacement:Play()
			selected = nil
		end)
		
		player.PlayerGui.ScreenGui.DeleteConfirmation.No.MouseButton1Click:Connect(function()
			client.OpenUI(player.PlayerGui.ScreenGui.DeleteConfirmation)
			selected = nil
		end)
	end
end)

player.PlayerGui.ScreenGui.MainButtons.BuildButton.Button.MouseButton1Click:Connect(function()
	if player.PlayerGui.ScreenGui.Building.Visible then
		DisableStructures()
	else
		DisplayStructures()
	end
end)

You have an extreme amount of connections you are not disconnecting. This is most likely causing huge memory leaks which will affect your game greatly. I disconnected all connections you aren’t using for you, but I’m not sure if it works until you test it.

Code:

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local client = require(game.StarterPlayer.StarterPlayerScripts:WaitForChild("Client"))
local ViewportModel = require(game.ReplicatedStorage.ViewportModel)

local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()

local placementEvent = game.ReplicatedStorage.RemoteEvents.PlacementEvent
local objFolder = game.ReplicatedStorage:WaitForChild("ObjectFolder")

local mouse = player:GetMouse()

local Frame = player.PlayerGui.ScreenGui.Building.ScrollingFrame
local itemPopup = player.PlayerGui.ScreenGui.BuildingItemPopup

local placingObject = false
local rotatingObject = false

local function DisplayStructures()
	for _, v in pairs(workspace.Plots[player.Name].Structures:GetChildren()) do
		local highlight = Instance.new("Highlight", v)
		highlight.OutlineColor = Color3.new(0.741176, 0.741176, 0.741176)
		highlight.FillColor = Color3.new(1, 1, 1)

		task.spawn(function()
			while task.wait(0.5) do
				local highlightTween = game:GetService("TweenService"):Create(highlight, TweenInfo.new(0.5), {FillColor = Color3.new(1, 1, 1)})
				highlightTween:Play()
				highlightTween.Completed:Wait()
				
				game:GetService("TweenService"):Create(highlight, TweenInfo.new(0.5), {FillColor = Color3.new(0.784314, 0.784314, 0.784314)}):Play()
			end
		end)
	end
end
local function DisableStructures()
	for _, v in pairs(game.Workspace.Plots[player.Name].Structures:GetDescendants()) do
		if v:IsA("Highlight") then
			v:Destroy()
		end
	end
end

local function isInsidePlot(position, plotPart)
	local plotSize = plotPart.Size / 2
	local plotCFrame = plotPart.CFrame

	local plotMin = plotCFrame.Position - plotSize
	local plotMax = plotCFrame.Position + plotSize

	return position.X >= plotMin.X and position.X <= plotMax.X
		and position.Z >= plotMin.Z and position.Z <= plotMax.Z
end

--Placement
local function EnablePlacement()
	for _, buttonFrame in pairs(Frame:GetChildren()) do
		if buttonFrame:IsA("Frame") then
			local button:ImageButton = buttonFrame.Button
			local connections = {}
			local nestedConnections = {}

			button.MouseButton1Click:Connect(function()
				for i, connection in connections do
					connection:Disconnect()
				end
				
				table.clear(connections)
				
				for i, connection in nestedConnections do
					connection:Disconnect()
				end

				table.clear(nestedConnections)
				
				itemPopup.Visible = true
				client.OpenUI(player.PlayerGui.ScreenGui.Building)
				DisableStructures()

				local object = game.ReplicatedStorage.ObjectFolder[buttonFrame.Name]

				itemPopup.ObjectName.Text = buttonFrame.Name
				itemPopup.Price.Text = buttonFrame.Cost.Text
				itemPopup.Desc.Text = object:GetAttribute("Desc")

				local camClone = object:Clone()
				local camera = Instance.new("Camera")
				camera.FieldOfView = 70
				camera.Parent = itemPopup.ViewportFrame

				camClone.Parent = itemPopup.ViewportFrame
				itemPopup.ViewportFrame.CurrentCamera = camera

				local vpfModel = ViewportModel.new(itemPopup.ViewportFrame, camera)
				local cf, size = camClone:GetBoundingBox()

				vpfModel:SetModel(camClone)

				local theta = 0
				local orientation = CFrame.new()
				local distance = vpfModel:GetFitDistance(cf.Position)

				table.insert(connections, game:GetService("RunService").RenderStepped:Connect(function(dt)
					theta = theta + math.rad(20 * dt)
					orientation = CFrame.fromEulerAnglesYXZ(math.rad(-20), theta, 0)
					camera.CFrame = CFrame.new(cf.Position) * orientation * CFrame.new(0, 0, distance)
				end))
								
				table.insert(connections, itemPopup.Place.MouseButton1Click:Connect(function()
					for i, connection in nestedConnections do
						connection:Disconnect()
					end

					table.clear(nestedConnections)
					
					camClone:Destroy()
					camera:Destroy()

					itemPopup.Visible = false

					if not placingObject then
						for _, sideButton in pairs(player.PlayerGui.ScreenGui.MainButtons:GetChildren()) do
							sideButton.Visible = false
						end
						for _, sideButton in pairs(player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren()) do
							sideButton.Visible = true
						end

						placingObject = true

						local rotationAmount = 0
						local gridSnapping = 0.5

						local previewObject = objFolder:FindFirstChild(buttonFrame.Name):Clone()
						previewObject.Parent = workspace

						for _, part in pairs(previewObject:GetDescendants()) do
							if part:IsA("BasePart") then
								part.Transparency = 0.5
								part.CanCollide = false
							end
							if part:IsA("ProximityPrompt") then
								part.Enabled = false
							end
						end

						table.insert(nestedConnections, UserInputService.InputBegan:Connect(function(key, gp)
							if not gp then
								if key.KeyCode == Enum.KeyCode.R then
									rotatingObject = true
									rotationAmount += 90

									game.SoundService.GridSnap:Play()
								end

								if key.KeyCode == Enum.KeyCode.X then
									if placingObject then
										rotatingObject = false
										placingObject = false
										previewObject:Destroy()
										client.OpenUI(player.PlayerGui.ScreenGui.Building)
										DisplayStructures()
										game.SoundService.CancelPlacement:Play()

										for _, sideButton in pairs(player.PlayerGui.ScreenGui.MainButtons:GetChildren()) do
											sideButton.Visible = true
										end
										for _, sideButton in pairs(player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren()) do
											sideButton.Visible = false
										end
									end
								end
							end
						end))

						table.insert(nestedConnections, UserInputService.InputEnded:Connect(function(key, gp)
							if key.KeyCode == Enum.KeyCode.R then
								rotatingObject = false
							end
						end))

						table.insert(nestedConnections, RunService.RenderStepped:Connect(function(deltaTime)
							if placingObject then
								mouse.TargetFilter = previewObject

								if previewObject:FindFirstChild("MainPart") then
									local ObjectCFrame = CFrame.new(math.round(mouse.hit.Position.X/gridSnapping)*gridSnapping, mouse.hit.Position.Y + previewObject.PrimaryPart.Size.Y/2, math.round(mouse.hit.Position.Z/gridSnapping)*gridSnapping)
									local ObjectAngles = CFrame.Angles(0,math.rad(rotationAmount), 0)

									if isInsidePlot(mouse.Hit.Position, game.Workspace.Plots[player.Name].PlotPart) then
										previewObject:SetPrimaryPartCFrame(ObjectCFrame * ObjectAngles)
									end
								end
							end
						end))

						table.insert(nestedConnections, mouse.Button1Up:Connect(function()
							if placingObject then
								if player.leaderstats.Cash.Value >= objFolder[buttonFrame.Name]:GetAttribute("Price") then
									placingObject = false

									game.SoundService.HammerAndNail:Play()

									for _, sideButton in pairs(player.PlayerGui.ScreenGui.MainButtons:GetChildren()) do
										sideButton.Visible = true
									end
									for _, sideButton in pairs(player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren()) do
										sideButton.Visible = false
									end

									placementEvent:FireServer(previewObject.Name, previewObject.PrimaryPart.CFrame)
									client.OpenUI(player.PlayerGui.ScreenGui.Building)
									DisplayStructures()
									previewObject:Destroy()
								else
									client.OpenUI(player.PlayerGui.ScreenGui.Building)
									DisplayStructures()
									previewObject:Destroy()
									placingObject = false

									for _, sideButton in pairs(player.PlayerGui.ScreenGui.MainButtons:GetChildren()) do
										sideButton.Visible = true
									end
									for _, sideButton in pairs(player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren()) do
										sideButton.Visible = false
									end
								end
							end
						end))
					end
				end))

				table.insert(connections, itemPopup.Cancel.MouseButton1Click:Connect(function()
					client.OpenUI(player.PlayerGui.ScreenGui.Building)
					camClone:Destroy()
					camera:Destroy()

					itemPopup.Visible = false
					DisplayStructures()
				end))
			end)
		end
	end
end

for _, object in pairs(game.ReplicatedStorage.ObjectFolder:GetChildren()) do
	local uiButton = game.ReplicatedStorage.BuildingItem:Clone()
	uiButton.Parent = player.PlayerGui.ScreenGui.Building.ScrollingFrame
	uiButton.ItemName.Text = object.Name
	uiButton.Cost.Text = "$"..tostring(object:GetAttribute("Price"))

	uiButton.Name = object.Name

	local camClone = object:Clone()

	local camera = Instance.new("Camera")
	camera.FieldOfView = 70
	camera.Parent = uiButton.ViewportFrame

	camClone.Parent = uiButton.ViewportFrame
	uiButton.ViewportFrame.CurrentCamera = camera

	local vpfModel = ViewportModel.new(uiButton.ViewportFrame, camera)
	local cf, size = camClone:GetBoundingBox()

	vpfModel:SetModel(camClone)

	local theta = 0
	local orientation = CFrame.new()
	local distance = vpfModel:GetFitDistance(cf.Position)

	game:GetService("RunService").RenderStepped:Connect(function(dt)
		theta = theta + math.rad(20 * dt)
		orientation = CFrame.fromEulerAnglesYXZ(math.rad(-20), theta, 0)
		camera.CFrame = CFrame.new(cf.Position) * orientation * CFrame.new(0, 0, distance)
	end)

	EnablePlacement()
end

for _, button in pairs(player.PlayerGui.ScreenGui.Building.Frame:GetChildren()) do
	if button:IsA("TextButton") then
		local displayConnections = {}
		--Display Buttons
		button.MouseButton1Click:Connect(function()
			game.SoundService.OpenUI:Play()
			for _, frame in pairs(player.PlayerGui.ScreenGui.Building.ScrollingFrame:GetChildren()) do
				if frame:IsA("Frame") then
					frame:Destroy()
				end
			end
			
			for i, connection in displayConnections do
				connection:Disconnect()
			end
			
			table.clear(displayConnections)

			if button.Name ~= "All" then
				for _, object in pairs(game.ReplicatedStorage.ObjectFolder:GetChildren()) do
					if object:GetAttribute("Type") == button.Name then
						local uiButton = game.ReplicatedStorage.BuildingItem:Clone()
						uiButton.Parent = player.PlayerGui.ScreenGui.Building.ScrollingFrame
						uiButton.ItemName.Text = object.Name
						uiButton.Cost.Text = "$"..tostring(object:GetAttribute("Price"))

						uiButton.Name = object.Name

						local camClone = object:Clone()

						local camera = Instance.new("Camera")
						camera.FieldOfView = 70
						camera.Parent = uiButton.ViewportFrame

						camClone.Parent = uiButton.ViewportFrame
						uiButton.ViewportFrame.CurrentCamera = camera

						local vpfModel = ViewportModel.new(uiButton.ViewportFrame, camera)
						local cf, size = camClone:GetBoundingBox()

						vpfModel:SetModel(camClone)

						local theta = 0
						local orientation = CFrame.new()
						local distance = vpfModel:GetFitDistance(cf.Position)

						table.insert(displayConnections, game:GetService("RunService").RenderStepped:Connect(function(dt)
							theta = theta + math.rad(20 * dt)
							orientation = CFrame.fromEulerAnglesYXZ(math.rad(-20), theta, 0)
							camera.CFrame = CFrame.new(cf.Position) * orientation * CFrame.new(0, 0, distance)
						end))
						
						EnablePlacement()
					end
				end
			else
				for _, object in pairs(game.ReplicatedStorage.ObjectFolder:GetChildren()) do
					local uiButton = game.ReplicatedStorage.BuildingItem:Clone()
					uiButton.Parent = player.PlayerGui.ScreenGui.Building.ScrollingFrame
					uiButton.ItemName.Text = object.Name
					uiButton.Cost.Text = "$"..tostring(object:GetAttribute("Price"))

					uiButton.Name = object.Name

					local camClone = object:Clone()

					local camera = Instance.new("Camera")
					camera.FieldOfView = 70
					camera.Parent = uiButton.ViewportFrame

					camClone.Parent = uiButton.ViewportFrame
					uiButton.ViewportFrame.CurrentCamera = camera

					local vpfModel = ViewportModel.new(uiButton.ViewportFrame, camera)
					local cf, size = camClone:GetBoundingBox()

					vpfModel:SetModel(camClone)

					local theta = 0
					local orientation = CFrame.new()
					local distance = vpfModel:GetFitDistance(cf.Position)

					table.insert(displayConnections, game:GetService("RunService").RenderStepped:Connect(function(dt)
						theta = theta + math.rad(20 * dt)
						orientation = CFrame.fromEulerAnglesYXZ(math.rad(-20), theta, 0)
						camera.CFrame = CFrame.new(cf.Position) * orientation * CFrame.new(0, 0, distance)
					end))

					EnablePlacement()
				end
			end
		end)
	end
end

-- Delete on MouseButton1Up
local selected
local confirmationConnections = {}

mouse.Button1Up:Connect(function()
	if player.PlayerGui.ScreenGui.Building.Visible and mouse.Target:FindFirstAncestor("Structures"):FindFirstAncestor(player.Name) and not selected then
		client.OpenUI(player.PlayerGui.ScreenGui.DeleteConfirmation)
		selected = mouse.Target.Parent 
		game.SoundService.OpenUI:Play()
		
		for i, connection in confirmationConnections do
			connection:Disconnect()
		end
		
		table.clear(confirmationConnections)

		table.insert(confirmationConnections, player.PlayerGui.ScreenGui.DeleteConfirmation.Yes.MouseButton1Click:Connect(function()
			client.OpenUI(player.PlayerGui.ScreenGui.DeleteConfirmation)
			selected:Destroy()
			game.SoundService.CancelPlacement:Play()
			selected = nil
		end))

		table.insert(confirmationConnections, player.PlayerGui.ScreenGui.DeleteConfirmation.No.MouseButton1Click:Connect(function()
			client.OpenUI(player.PlayerGui.ScreenGui.DeleteConfirmation)
			selected = nil
		end))
	end
end)

player.PlayerGui.ScreenGui.MainButtons.BuildButton.Button.MouseButton1Click:Connect(function()
	if player.PlayerGui.ScreenGui.Building.Visible then
		DisableStructures()
	else
		DisplayStructures()
	end
end)

Example of a memory leak:

button1.MouseButton1Down:Connect(function()
	button2.MouseButton1Down:Connect(function()

	end)
	
	button3.MouseButton1Down:Connect(function()

	end)
end)

Every time you click button1, it creates new events to button2 and button3, none of which are disconnected upon clicking again, which causes a memory leak.

2 Likes

Thank you! All the issues I’ve had before have been mostly resolved after your changes. I will make sure to clear up any memory leaks from this point on.

Although, as I continue to use my build system the Steps per Second do seem to go down over time. I’m not sure if this is expected in studio or what this really means. But all other issues like Axe cooldown, UI delays etc are fixed.

Steps per second will naturally go down if there are many instances. If it’s too much, then yeah it’s a problem but if it’s fairly little, then it should be fine.

I also removed some more memory leaks:

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local client = require(game.StarterPlayer.StarterPlayerScripts:WaitForChild("Client"))
local ViewportModel = require(game.ReplicatedStorage.ViewportModel)

local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()

local placementEvent = game.ReplicatedStorage.RemoteEvents.PlacementEvent
local objFolder = game.ReplicatedStorage:WaitForChild("ObjectFolder")

local mouse = player:GetMouse()

local Frame = player.PlayerGui.ScreenGui.Building.ScrollingFrame
local itemPopup = player.PlayerGui.ScreenGui.BuildingItemPopup

local placingObject = false
local rotatingObject = false

local function DisplayStructures()
	for _, v in workspace.Plots[player.Name].Structures:GetChildren() do
		if v:FindFirstChildWhichIsA("Highlight") then
			continue
		end
		
		local highlight = Instance.new("Highlight")
		highlight.OutlineColor = Color3.new(0.741176, 0.741176, 0.741176)
		highlight.FillColor = Color3.new(1, 1, 1)
		highlight.Parent = v

		task.spawn(function()
			while task.wait(0.5) do
				local highlightTween = game:GetService("TweenService"):Create(highlight, TweenInfo.new(0.5), {FillColor = Color3.new(1, 1, 1)})
				highlightTween:Play()
				highlightTween.Completed:Wait()

				game:GetService("TweenService"):Create(highlight, TweenInfo.new(0.5), {FillColor = Color3.new(0.784314, 0.784314, 0.784314)}):Play()
			end
		end)
	end
end
local function DisableStructures()
	for _, v in game.Workspace.Plots[player.Name].Structures:GetDescendants() do
		if v:IsA("Highlight") then
			v:Destroy()
		end
	end
end

local function isInsidePlot(position, plotPart)
	local plotSize = plotPart.Size / 2
	local plotCFrame = plotPart.CFrame

	local plotMin = plotCFrame.Position - plotSize
	local plotMax = plotCFrame.Position + plotSize

	return position.X >= plotMin.X and position.X <= plotMax.X
		and position.Z >= plotMin.Z and position.Z <= plotMax.Z
end

--Placement
local buttonConnections = {}
local nestedButtonConnections = {
	Connections = {},
	NestedConnections = {}
}

local function EnablePlacement()
	for i, connection in buttonConnections do
		connection:Disconnect()
	end
	
	table.clear(buttonConnections)
	
	for connectionType, connections in nestedButtonConnections do
		for i, connection in connections do
			connection:Disconnect()
		end
		
		table.clear(nestedButtonConnections[connectionType])
	end
	
	for _, buttonFrame in Frame:GetChildren() do
		if buttonFrame:IsA("Frame") then
			local button:ImageButton = buttonFrame.Button

			table.insert(buttonConnections, button.MouseButton1Click:Connect(function()
				for connectionType, connections in nestedButtonConnections do
					for i, connection in connections do
						connection:Disconnect()
					end

					table.clear(nestedButtonConnections[connectionType])
				end

				itemPopup.Visible = true
				client.OpenUI(player.PlayerGui.ScreenGui.Building)
				DisableStructures()

				local object = game.ReplicatedStorage.ObjectFolder[buttonFrame.Name]

				itemPopup.ObjectName.Text = buttonFrame.Name
				itemPopup.Price.Text = buttonFrame.Cost.Text
				itemPopup.Desc.Text = object:GetAttribute("Desc")

				local camClone = object:Clone()
				local camera = Instance.new("Camera")
				camera.FieldOfView = 70
				camera.Parent = itemPopup.ViewportFrame

				camClone.Parent = itemPopup.ViewportFrame
				itemPopup.ViewportFrame.CurrentCamera = camera

				local vpfModel = ViewportModel.new(itemPopup.ViewportFrame, camera)
				local cf, size = camClone:GetBoundingBox()

				vpfModel:SetModel(camClone)

				local theta = 0
				local orientation = CFrame.new()
				local distance = vpfModel:GetFitDistance(cf.Position)

				table.insert(nestedButtonConnections.Connections, game:GetService("RunService").RenderStepped:Connect(function(dt)
					theta = theta + math.rad(20 * dt)
					orientation = CFrame.fromEulerAnglesYXZ(math.rad(-20), theta, 0)
					camera.CFrame = CFrame.new(cf.Position) * orientation * CFrame.new(0, 0, distance)
				end))

				table.insert(nestedButtonConnections.Connections, itemPopup.Place.MouseButton1Click:Connect(function()
					for i, connection in nestedButtonConnections.NestedConnections do
						connection:Disconnect()
					end

					table.clear(nestedButtonConnections.NestedConnections)

					camClone:Destroy()
					camera:Destroy()

					itemPopup.Visible = false

					if not placingObject then
						for _, sideButton in player.PlayerGui.ScreenGui.MainButtons:GetChildren() do
							sideButton.Visible = false
						end
						for _, sideButton in player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren() do
							sideButton.Visible = true
						end

						placingObject = true

						local rotationAmount = 0
						local gridSnapping = 0.5

						local previewObject = objFolder:FindFirstChild(buttonFrame.Name):Clone()
						previewObject.Parent = workspace

						for _, part in previewObject:GetDescendants() do
							if part:IsA("BasePart") then
								part.Transparency = 0.5
								part.CanCollide = false
							end
							if part:IsA("ProximityPrompt") then
								part.Enabled = false
							end
						end

						table.insert(nestedButtonConnections.NestedConnections, UserInputService.InputBegan:Connect(function(key, gp)
							if not gp then
								if key.KeyCode == Enum.KeyCode.R then
									rotatingObject = true
									rotationAmount += 90

									game.SoundService.GridSnap:Play()
								end

								if key.KeyCode == Enum.KeyCode.X then
									if placingObject then
										rotatingObject = false
										placingObject = false
										previewObject:Destroy()
										client.OpenUI(player.PlayerGui.ScreenGui.Building)
										DisplayStructures()
										game.SoundService.CancelPlacement:Play()

										for _, sideButton in player.PlayerGui.ScreenGui.MainButtons:GetChildren() do
											sideButton.Visible = true
										end
										for _, sideButton in player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren() do
											sideButton.Visible = false
										end
									end
								end
							end
						end))

						table.insert(nestedButtonConnections.NestedConnections, UserInputService.InputEnded:Connect(function(key, gp)
							if key.KeyCode == Enum.KeyCode.R then
								rotatingObject = false
							end
						end))

						table.insert(nestedButtonConnections.NestedConnections, RunService.RenderStepped:Connect(function(deltaTime)
							if placingObject then
								mouse.TargetFilter = previewObject

								if previewObject:FindFirstChild("MainPart") then
									local ObjectCFrame = CFrame.new(math.round(mouse.hit.Position.X/gridSnapping)*gridSnapping, mouse.hit.Position.Y + previewObject.PrimaryPart.Size.Y/2, math.round(mouse.hit.Position.Z/gridSnapping)*gridSnapping)
									local ObjectAngles = CFrame.Angles(0,math.rad(rotationAmount), 0)

									if isInsidePlot(mouse.Hit.Position, game.Workspace.Plots[player.Name].PlotPart) then
										previewObject:SetPrimaryPartCFrame(ObjectCFrame * ObjectAngles)
									end
								end
							end
						end))

						table.insert(nestedButtonConnections.NestedConnections, mouse.Button1Up:Connect(function()
							if placingObject then
								if player.leaderstats.Cash.Value >= objFolder[buttonFrame.Name]:GetAttribute("Price") then
									placingObject = false

									game.SoundService.HammerAndNail:Play()

									for _, sideButton in player.PlayerGui.ScreenGui.MainButtons:GetChildren() do
										sideButton.Visible = true
									end
									for _, sideButton in player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren() do
										sideButton.Visible = false
									end

									placementEvent:FireServer(previewObject.Name, previewObject.PrimaryPart.CFrame)
									client.OpenUI(player.PlayerGui.ScreenGui.Building)
									DisplayStructures()
									previewObject:Destroy()
								else
									client.OpenUI(player.PlayerGui.ScreenGui.Building)
									DisplayStructures()
									previewObject:Destroy()
									placingObject = false

									for _, sideButton in player.PlayerGui.ScreenGui.MainButtons:GetChildren() do
										sideButton.Visible = true
									end
									for _, sideButton in player.PlayerGui.ScreenGui.BuildingToolTips:GetChildren() do
										sideButton.Visible = false
									end
								end
							end
						end))
					end
				end))

				table.insert(nestedButtonConnections.Connections, itemPopup.Cancel.MouseButton1Click:Connect(function()
					client.OpenUI(player.PlayerGui.ScreenGui.Building)
					camClone:Destroy()
					camera:Destroy()

					itemPopup.Visible = false
					DisplayStructures()
				end))
			end))
		end
	end
end

for _, object in game.ReplicatedStorage.ObjectFolder:GetChildren() do
	local uiButton = game.ReplicatedStorage.BuildingItem:Clone()
	uiButton.Parent = player.PlayerGui.ScreenGui.Building.ScrollingFrame
	uiButton.ItemName.Text = object.Name
	uiButton.Cost.Text = "$"..tostring(object:GetAttribute("Price"))

	uiButton.Name = object.Name

	local camClone = object:Clone()

	local camera = Instance.new("Camera")
	camera.FieldOfView = 70
	camera.Parent = uiButton.ViewportFrame

	camClone.Parent = uiButton.ViewportFrame
	uiButton.ViewportFrame.CurrentCamera = camera

	local vpfModel = ViewportModel.new(uiButton.ViewportFrame, camera)
	local cf, size = camClone:GetBoundingBox()

	vpfModel:SetModel(camClone)

	local theta = 0
	local orientation = CFrame.new()
	local distance = vpfModel:GetFitDistance(cf.Position)

	game:GetService("RunService").RenderStepped:Connect(function(dt)
		theta = theta + math.rad(20 * dt)
		orientation = CFrame.fromEulerAnglesYXZ(math.rad(-20), theta, 0)
		camera.CFrame = CFrame.new(cf.Position) * orientation * CFrame.new(0, 0, distance)
	end)

	EnablePlacement()
end

for _, button in player.PlayerGui.ScreenGui.Building.Frame:GetChildren() do
	if button:IsA("TextButton") then
		local displayConnections = {}
		--Display Buttons
		button.MouseButton1Click:Connect(function()
			game.SoundService.OpenUI:Play()
			for _, frame in player.PlayerGui.ScreenGui.Building.ScrollingFrame:GetChildren() do
				if frame:IsA("Frame") then
					frame:Destroy()
				end
			end

			for i, connection in displayConnections do
				connection:Disconnect()
			end

			table.clear(displayConnections)

			if button.Name ~= "All" then
				for _, object in game.ReplicatedStorage.ObjectFolder:GetChildren() do
					if object:GetAttribute("Type") == button.Name then
						local uiButton = game.ReplicatedStorage.BuildingItem:Clone()
						uiButton.Parent = player.PlayerGui.ScreenGui.Building.ScrollingFrame
						uiButton.ItemName.Text = object.Name
						uiButton.Cost.Text = "$"..tostring(object:GetAttribute("Price"))

						uiButton.Name = object.Name

						local camClone = object:Clone()

						local camera = Instance.new("Camera")
						camera.FieldOfView = 70
						camera.Parent = uiButton.ViewportFrame

						camClone.Parent = uiButton.ViewportFrame
						uiButton.ViewportFrame.CurrentCamera = camera

						local vpfModel = ViewportModel.new(uiButton.ViewportFrame, camera)
						local cf, size = camClone:GetBoundingBox()

						vpfModel:SetModel(camClone)

						local theta = 0
						local orientation = CFrame.new()
						local distance = vpfModel:GetFitDistance(cf.Position)

						table.insert(displayConnections, game:GetService("RunService").RenderStepped:Connect(function(dt)
							theta = theta + math.rad(20 * dt)
							orientation = CFrame.fromEulerAnglesYXZ(math.rad(-20), theta, 0)
							camera.CFrame = CFrame.new(cf.Position) * orientation * CFrame.new(0, 0, distance)
						end))

						EnablePlacement()
					end
				end
			else
				for _, object in game.ReplicatedStorage.ObjectFolder:GetChildren() do
					local uiButton = game.ReplicatedStorage.BuildingItem:Clone()
					uiButton.Parent = player.PlayerGui.ScreenGui.Building.ScrollingFrame
					uiButton.ItemName.Text = object.Name
					uiButton.Cost.Text = "$"..tostring(object:GetAttribute("Price"))

					uiButton.Name = object.Name

					local camClone = object:Clone()

					local camera = Instance.new("Camera")
					camera.FieldOfView = 70
					camera.Parent = uiButton.ViewportFrame

					camClone.Parent = uiButton.ViewportFrame
					uiButton.ViewportFrame.CurrentCamera = camera

					local vpfModel = ViewportModel.new(uiButton.ViewportFrame, camera)
					local cf, size = camClone:GetBoundingBox()

					vpfModel:SetModel(camClone)

					local theta = 0
					local orientation = CFrame.new()
					local distance = vpfModel:GetFitDistance(cf.Position)

					table.insert(displayConnections, game:GetService("RunService").RenderStepped:Connect(function(dt)
						theta = theta + math.rad(20 * dt)
						orientation = CFrame.fromEulerAnglesYXZ(math.rad(-20), theta, 0)
						camera.CFrame = CFrame.new(cf.Position) * orientation * CFrame.new(0, 0, distance)
					end))

					EnablePlacement()
				end
			end
		end)
	end
end

-- Delete on MouseButton1Up
local selected
local confirmationConnections = {}

mouse.Button1Up:Connect(function()
	if player.PlayerGui.ScreenGui.Building.Visible and mouse.Target:FindFirstAncestor("Structures"):FindFirstAncestor(player.Name) and not selected then
		client.OpenUI(player.PlayerGui.ScreenGui.DeleteConfirmation)
		selected = mouse.Target.Parent 
		game.SoundService.OpenUI:Play()

		for i, connection in confirmationConnections do
			connection:Disconnect()
		end

		table.clear(confirmationConnections)

		table.insert(confirmationConnections, player.PlayerGui.ScreenGui.DeleteConfirmation.Yes.MouseButton1Click:Connect(function()
			client.OpenUI(player.PlayerGui.ScreenGui.DeleteConfirmation)
			selected:Destroy()
			game.SoundService.CancelPlacement:Play()
			selected = nil
		end))

		table.insert(confirmationConnections, player.PlayerGui.ScreenGui.DeleteConfirmation.No.MouseButton1Click:Connect(function()
			client.OpenUI(player.PlayerGui.ScreenGui.DeleteConfirmation)
			selected = nil
		end))
	end
end)

player.PlayerGui.ScreenGui.MainButtons.BuildButton.Button.MouseButton1Click:Connect(function()
	if player.PlayerGui.ScreenGui.Building.Visible then
		DisableStructures()
	else
		DisplayStructures()
	end
end)

If you are satisfied with the performance, please set my reply as the solution.

Awesome, I’ll try out your improved code and let you know

You can also use the Micro Profiler for a in depth “graph” of the resources being taken
image
Press Ctrl F6
Press Ctrl P
Click somewhere at the right here
image
(The green is the frames you are looking at)

Now, you should be able to see all the scripts and other things
image
The lenght of the lines is how much time they take to run, in my case, heartbeat takes around 1.5ms
At the top it basically shows what length equals to 1ms (if you zoom in or out, it will show more or less than 1ms)

It might be hard to find your scripts at first, but look for Hearbeat and PreRender
Under Heartbeat will be whatever you binded to Heartbeat, under PreRender will be functions bound to BindToRenderStepped and RunService.RenderStepped

If you manage to be able to use the Micro Profiler, it will take out a lot of the guessing when fixing lag issues

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.