My game lags and viewports stops working

  1. What do you want to achieve? I made a system where you could buy towers from shop

  2. What is the issue? My game lags when i one or twice click buyTowerButton and shop viewport dissapears

  3. What solutions have you tried so far? I looked and i found no similar to my issue in dev forum

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")

local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))

local playerData = {}

local functions = ReplicatedStorage:WaitForChild("Functions")
local remoteFunctions = functions.Remote
local getDataFunction = remoteFunctions.GetData
local interactItemFunction = remoteFunctions.InteractItem

local gui = script.Parent
local exitButton = gui.Shop.ExitGUIFrame.Button
local shopButton = gui.Parent.OptionsGUI.ShopButton.Button
local buyTowerButton = gui.Shop.Main.Info.Tower.TowerInfo.BuyTowerFrame.Button
local itemsFrame = gui.Shop.Main.Content.TowerContent

local towerInfo = gui.Shop.Main.Info.Tower
local updateDebounce = false
local previousTower = nil

local function interactItem(itemName)
	local data = interactItemFunction:InvokeServer(itemName)
	if data then
		playerData = data
		updateShopItems()
	end
end

local function setAnimation(object, folderName, animName)
	local humanoid = object:WaitForChild("Humanoid")
	local animationsFolder = object:WaitForChild("Animations"):WaitForChild(folderName)

	if humanoid and animationsFolder then
		local AnimationObject = animationsFolder:WaitForChild(animName)
		if AnimationObject then
			local animator = humanoid:FindFirstChild("Animator") or Instance.new("Animator", humanoid)

			local playingTracks = animator:GetPlayingAnimationTracks()
			for _, track in ipairs(playingTracks) do
				if track.Name == animName then
					return track
				end
			end

			local success, AnimationTrack
			repeat
				success, AnimationTrack = pcall(function()
					return animator:LoadAnimation(AnimationObject)
				end)

				if not success then task.wait(0.2) end
			until success

			return AnimationTrack
		end
	end
end

local function playAnimation(object, folderName, animName)
	local AnimationTrack = setAnimation(object, folderName, animName)
	if AnimationTrack then
		AnimationTrack:Play()
	else
		warn("Animation track does not exist..")
	end
end

local function getItemStatus(itemName)
	if table.find(playerData.EquippedTowers, itemName) then
		return "Equipped"
	elseif table.find(playerData.OwnedTowers, itemName) then
		return "Owned"
	else
		return "For Sale"
	end
end

function updateTowerInfo(tower)
	if previousTower and previousTower.Name == tower.Name then
		return
	end
	previousTower = tower

	towerInfo.TowerInfo.TowerFrame.TowerName.Text = tower.Name
	towerInfo.Description.Content.DescriptionText.Text = tower.Description

	local viewportFrame = towerInfo.TowerInfo.TowerFrame.ViewportFrame
	local worldModel = viewportFrame:FindFirstChild("WorldModel") or Instance.new("WorldModel", viewportFrame)
	local towerViewportCamera = viewportFrame:FindFirstChild("Camera") or Instance.new("Camera", viewportFrame)
	viewportFrame.CurrentCamera = towerViewportCamera

	for _, child in ipairs(worldModel:GetChildren()) do
		if child:IsA("Model") then
			child:Destroy()
		end
	end

	local towerTemplate = tower.ViewportModel:Clone()
	towerTemplate.Parent = worldModel

	local config = towerTemplate.Config
	towerInfo.TowerInfo.TowerStats.DamageFrame.Value.Text = tostring(config.Damage.Value)
	towerInfo.TowerInfo.TowerStats.CooldownFrame.Value.Text = tostring(config.Firerate.Value)
	towerInfo.TowerInfo.TowerStats.RangeFrame.Value.Text = tostring(config.Range.Value)

	if config:FindFirstChild("SeeHidden") then
		towerInfo.TowerInfo.TowerStats.ExtraFrame.Value.Text = "Yes"
	else
		towerInfo.TowerInfo.TowerStats.ExtraFrame.Value.Text = "No"
	end

	local primaryPart = towerTemplate.PrimaryPart
	towerViewportCamera.CFrame = primaryPart.CFrame * CFrame.Angles(math.rad(10), math.rad(200), 0) * CFrame.new(0, 0, 2)
	playAnimation(towerTemplate, "Idle", "Idle")

	local towerStatus = getItemStatus(tower.Name)
	local button = towerInfo.TowerInfo.BuyTowerFrame.Button
	local buttonBg = towerInfo.TowerInfo.BuyTowerFrame.ButtonBackground
	local buttonText = ""

	if towerStatus == "For Sale" then
		button.BackgroundColor3 = Color3.fromRGB(0, 255, 0)
		button.MainUIStroke.Color = Color3.fromRGB(0, 0, 0)
		buttonBg.BackgroundColor3 = Color3.fromRGB(0, 255, 0)
		buttonBg.UIStroke.Color = Color3.fromRGB(0, 170, 0)
		buttonText = "Buy: $" .. tower.Price
	elseif towerStatus == "Equipped" then
		button.BackgroundColor3 = Color3.fromRGB(175, 175, 175)
		button.MainUIStroke.Color = Color3.fromRGB(0, 0, 0)
		buttonBg.BackgroundColor3 = Color3.fromRGB(175, 175, 175)
		buttonBg.UIStroke.Color = Color3.fromRGB(145, 145, 145)
		buttonText = "Unequip?"
	elseif towerStatus == "Owned" then
		button.BackgroundColor3 = Color3.fromRGB(255, 140, 0)
		button.MainUIStroke.Color = Color3.fromRGB(0, 0, 0)
		buttonBg.BackgroundColor3 = Color3.fromRGB(255, 140, 0)
		buttonBg.UIStroke.Color = Color3.fromRGB(198, 92, 0)
		buttonText = "Equip?"
	end
	button.Text = buttonText
end

function updateShopItems()
	if updateDebounce then return end
	updateDebounce = true

	local items = itemsFrame:GetChildren()
	for i = #items, 1, -1 do
		local child = items[i]
		if child:IsA("Frame") and child.Name ~= "TemplateSlot" then
			child:Destroy()
		end
	end

	for _, tower in pairs(towers) do
		local newButton = itemsFrame.TemplateSlot:Clone()
		newButton.Name = tower.Name
		newButton.TowerName.Text = tower.Name
		newButton.LayoutOrder = tower.Price
		newButton.Visible = true
		newButton.Parent = itemsFrame

		if tower.ViewportModel then
			local towerTemplate = tower.ViewportModel:Clone()
			local towerViewportCamera = newButton.ViewportFrame:FindFirstChild("Camera") or Instance.new("Camera", newButton.ViewportFrame)
			newButton.ViewportFrame.CurrentCamera = towerViewportCamera

			towerTemplate.Parent = newButton.ViewportFrame:FindFirstChild("WorldModel") or Instance.new("WorldModel", newButton.ViewportFrame)
			towerViewportCamera.CFrame = towerTemplate.PrimaryPart.CFrame * CFrame.Angles(math.rad(10), math.rad(200), 0) * CFrame.new(0, 0, 2)
			playAnimation(towerTemplate, "Idle", "Idle")
		end

		newButton.Button.Activated:Connect(function()
			towerInfo.Visible = true
			updateTowerInfo(tower)
		end)
		buyTowerButton.Activated:Connect(function()
			interactItem(tower.Name)
		end)
	end

	updateDebounce = false
end

local function toggleShopItems()
	if gui.Visible then
		playerData = getDataFunction:InvokeServer()
		updateShopItems()
		gui.Shop.Visible = true
	else
		gui.Shop.Visible = false
	end
end

exitButton.Activated:Connect(function()
	gui.Visible = false
end)

shopButton.Activated:Connect(function()
	gui.Visible = not gui.Visible
	toggleShopItems()
end)