I am creating a Tower Defense game, I have a storage system and I want that when I select a certain tower, I have a frame with a description and statistics that corresponds to the selected tower.
It should be like this
But when I click on another tower, the frames begin to layer on top of each other and create even more copies.
function updateItems()
StatsGui.MainFrame.Coins.TextLabel.Text = playerData.Coins
StatsGui.MainFrame.Towers.TextLabel.Text = #playerData.SelectedTowers.."/5"
for i, tower in pairs(towers) do
local oldButton = CoinTowerList:FindFirstChild(tower.Name)
if oldButton then
oldButton:Destroy()
end
local oldDesc = TowerDescFrame:FindFirstChild(tower.Name)
if oldDesc then
oldDesc:Destroy(tower.Name)
end
local newButton = CoinTowerList.TowerTemplate:Clone()
newButton.Name = tower.Name
newButton.TowerName.Text = tower.Name
newButton.TowerImage.Image = tower.ImageAsset
newButton.Visible = true
newButton.LayoutOrder = tower.Price
newButton.Parent = CoinTowerList
local newDescriptionTower = TowerDescFrame.Frame2:Clone()
newDescriptionTower.Name = tower.Name
newDescriptionTower.TowerName.Text = tower.Name
newDescriptionTower.ImageLabel.Image = tower.ImageAsset
newDescriptionTower.ScrollingFrame.Description.StatsText.Text = tower.Description
newDescriptionTower.ScrollingFrame.Stats.CooldownText.Text = tower.Cooldown
newDescriptionTower.ScrollingFrame.Stats.DamageText.Text = tower.Damage
newDescriptionTower.ScrollingFrame.Stats.RangeText.Text = tower.Range
newDescriptionTower.ScrollingFrame.Stats.MoneyText.Text = tower.MoneyPrice
newDescriptionTower.Visible = false
newDescriptionTower.Parent = gui.MainShopFrame.ShopFrame.ShopDesc.TowerDesc.Frame
local status = getItemStatus(tower.Name)
if status == "For Sale" then
newButton.TowerImage.Price.Text = tower.Price
newButton.TowerImage.CoinIcon.Image = "rbxassetid://"
elseif status == "Equipped" then
newButton.TowerImage.Price.Text = "Equipped"
newDescriptionTower.TextButton.TextLabel.Text = "Equipped"
newButton.TowerImage.CoinIcon.Image = "rbxassetid://"
newButton.TowerImage.Price.PriceGradient.Enabled = false
newButton.TowerImage.Price.EquippedGradient.Enabled = true
else
newButton.TowerImage.Price.Text = ""
newButton.TowerImage.CoinIcon.Visible = false
end
newButton.Activated:Connect(function()
interactItem(tower.Name)
if newDescriptionTower.Name == newButton.Name then
newDescriptionTower.Visible = true
end
end)
end
end
Is it possible to make it so that when a certain tower is selected, only one frame with a description and statistics is created that corresponds to the selected tower, and then deleted if another tower was selected?
There is a property called ZIndex. I highly recommend trying to use it. If that doesn’t work, post here.
Another solution to try is create a variable called currentStatsFrame for your currently selected frame. If another tower is selected, simply call :Destroy() if currentStatsFrame ~= nil. (Make sure to assign it with :Copy, for example currentStatsFrame = towerStatsFrame:Copy())
I know about ZIndex, but that’s not what I need. As I described earlier, I need that when I select a tower, I have only one frame corresponding to the selected tower, which will exist until another tower is selected.
Not only that, there is also a toggleShop function
local function toggleShop(player)
gui.MainShopFrame.Visible = not gui.MainShopFrame.Visible
if gui.MainShopFrame.Visible then
playerData = getDataFunc:InvokeServer()
updateItems()
end
end
Please post the full code for everything related to the TowerGUI, without it we can’t really help you since new related code keeps popping up. But i think i see the issue, give me a moment.
EDIT:
Could you adjust your oldDesc code to this?
Maybe its used incorrectly.
local oldDesc = TowerDescFrame:FindFirstChild(tower.Name)
if oldDesc then
oldDesc:Destroy()
end
I indicated everything that is responsible for the interface with towers at the beginning of the topic, but if it’s easier, then here’s the full code
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))
local getDataFunc = ReplicatedStorage.RemoteFunctions:WaitForChild("GetData")
local interactItemFunc = ReplicatedStorage.RemoteFunctions:WaitForChild("InteractItem")
local gui = script.Parent
local exit = gui.MainShopFrame.ShopFrame.CloseButton
local menu = gui.MainShopFrame.ShopFrame.ShopMenuButton
local CoinTowerList = gui.MainShopFrame.ShopFrame.ShopDesc.BuyList.ScrollingFrame.CoinTowers.TowerList
local TowerDescFrame = gui.MainShopFrame.ShopFrame.ShopDesc.TowerDesc.Frame
local shopPromt = workspace.ShopZone.TowerShop.VenoxDevs:WaitForChild("Head")
local StatsGui = gui.StatsGui
local selectedTower = nil
local playerData = {}
local function getItemStatus(itemName)
if table.find(playerData.SelectedTowers, itemName) then
return "Equipped"
elseif table.find(playerData.OwnedTowers, itemName) then
return "Owned"
else
return "For Sale"
end
end
local function interactItem(itemName)
local data = interactItemFunc:InvokeServer(itemName)
if data then
playerData = data
updateItems()
end
end
function updateItems()
StatsGui.MainFrame.Coins.TextLabel.Text = playerData.Coins
StatsGui.MainFrame.Towers.TextLabel.Text = #playerData.SelectedTowers.."/5"
for i, tower in pairs(towers) do
local oldButton = CoinTowerList:FindFirstChild(tower.Name)
if oldButton then
oldButton:Destroy()
end
local oldDesc = TowerDescFrame:FindFirstChild(tower.Name)
if oldDesc then
--oldDesc:Destroy(tower.Name)
end
local oldDesc2 = TowerDescFrame.Parent.FrameOld:FindFirstChild(tower.Name)
if oldDesc2 then
oldDesc2:Destroy(tower.Name)
end
local newButton = CoinTowerList.TowerTemplate:Clone()
newButton.Name = tower.Name
newButton.TowerName.Text = tower.Name
newButton.TowerImage.Image = tower.ImageAsset
newButton.Visible = true
newButton.LayoutOrder = tower.Price
newButton.Parent = CoinTowerList
local newDescriptionTower = TowerDescFrame.Frame2:Clone()
newDescriptionTower.Name = tower.Name
newDescriptionTower.TowerName.Text = tower.Name
newDescriptionTower.ImageLabel.Image = tower.ImageAsset
newDescriptionTower.ScrollingFrame.Description.StatsText.Text = tower.Description
newDescriptionTower.ScrollingFrame.Stats.CooldownText.Text = tower.Cooldown
newDescriptionTower.ScrollingFrame.Stats.DamageText.Text = tower.Damage
newDescriptionTower.ScrollingFrame.Stats.RangeText.Text = tower.Range
newDescriptionTower.ScrollingFrame.Stats.MoneyText.Text = tower.MoneyPrice
newDescriptionTower.Visible = false
newDescriptionTower.Parent = gui.MainShopFrame.ShopFrame.ShopDesc.TowerDesc.Frame
local status = getItemStatus(tower.Name)
if status == "For Sale" then
newButton.TowerImage.Price.Text = tower.Price
newButton.TowerImage.CoinIcon.Image = "rbxassetid://"
elseif status == "Equipped" then
newButton.TowerImage.Price.Text = "Equipped"
newDescriptionTower.TextButton.TextLabel.Text = "Equipped"
newButton.TowerImage.CoinIcon.Image = "rbxassetid://"
newButton.TowerImage.Price.PriceGradient.Enabled = false
newButton.TowerImage.Price.EquippedGradient.Enabled = true
else
newButton.TowerImage.Price.Text = ""
newButton.TowerImage.CoinIcon.Visible = false
end
newButton.Activated:Connect(function()
interactItem(tower.Name)
if newDescriptionTower.Name == newButton.Name then
newDescriptionTower.Visible = true
elseif newDescriptionTower.Visible == false then
newDescriptionTower.Parent = TowerDescFrame.Parent.FrameOld
end
end)
end
end
local function toggleShop(player)
gui.MainShopFrame.Visible = not gui.MainShopFrame.Visible
if gui.MainShopFrame.Visible then
playerData = getDataFunc:InvokeServer()
updateItems()
end
end
local function setupShop()
local promt = Instance.new("ProximityPrompt")
promt.RequiresLineOfSight = false
promt.ActionText = "Shop"
promt.Parent = shopPromt
promt.Triggered:Connect(toggleShop)
exit.Activated:Connect(toggleShop)
menu.Activated:Connect(function()
gui.MainShopFrame.MenuFrame.Visible = not gui.MainShopFrame.MenuFrame.Visible
end)
end
setupShop()
In general, every time the store is updated, and this includes clicking on any tower in the list, the list of towers begins to clone itself, so duplicates appear. This code block removes duplicates.
I tried to do it by analogy with oldButton, but it doesn’t quite work properly
local oldButton = CoinTowerList:FindFirstChild(tower.Name)
if oldButton then
oldButton:Destroy() -- This gets destroyed every time
end
local oldDesc = TowerDescFrame:FindFirstChild(tower.Name)
if oldDesc then
oldDesc:Destroy(tower.Name) -- You're having issues with this
end
See how the oldButton uses :Destroy() and the oldDesc uses :Destroy(tower.Name)?
Now since the oldButton does get destroyed every time, try also just making the oldDesc:Destroy().
This would be the final code for it:
local oldDesc = TowerDescFrame:FindFirstChild(tower.Name)
if oldDesc then
oldDesc:Destroy() -- No more tower.name, It might work
end
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))
local getDataFunc = ReplicatedStorage.RemoteFunctions:WaitForChild("GetData")
local interactItemFunc = ReplicatedStorage.RemoteFunctions:WaitForChild("InteractItem")
local gui = script.Parent
local exit = gui.MainShopFrame.ShopFrame.CloseButton
local menu = gui.MainShopFrame.ShopFrame.ShopMenuButton
local CoinTowerList = gui.MainShopFrame.ShopFrame.ShopDesc.BuyList.ScrollingFrame.CoinTowers.TowerList
local TowerDescFrame = gui.MainShopFrame.ShopFrame.ShopDesc.TowerDesc.Frame
local shopPromt = workspace.ShopZone.TowerShop.VenoxDevs:WaitForChild("Head")
local StatsGui = gui.StatsGui
local selectedTower = nil
local playerData = {}
local function getItemStatus(itemName)
if table.find(playerData.SelectedTowers, itemName) then
return "Equipped"
elseif table.find(playerData.OwnedTowers, itemName) then
return "Owned"
else
return "For Sale"
end
end
local function interactItem(itemName)
local data = interactItemFunc:InvokeServer(itemName)
if data then
playerData = data
updateItems()
end
end
function updateItems()
StatsGui.MainFrame.Coins.TextLabel.Text = playerData.Coins
StatsGui.MainFrame.Towers.TextLabel.Text = #playerData.SelectedTowers.."/5"
for i, tower in pairs(towers) do
local oldButton = CoinTowerList:FindFirstChild(tower.Name)
if oldButton then
oldButton:Destroy()
end
local oldDesc = TowerDescFrame:FindFirstChild(tower.Name)
if oldDesc then
oldDesc:Destroy()
end
local newButton = CoinTowerList.TowerTemplate:Clone()
newButton.Name = tower.Name
newButton.TowerName.Text = tower.Name
newButton.TowerImage.Image = tower.ImageAsset
newButton.Visible = true
newButton.LayoutOrder = tower.Price
newButton.Parent = CoinTowerList
local newDescriptionTower = TowerDescFrame.Frame2:Clone()
newDescriptionTower.Name = tower.Name
newDescriptionTower.TowerName.Text = tower.Name
newDescriptionTower.ImageLabel.Image = tower.ImageAsset
newDescriptionTower.ScrollingFrame.Description.StatsText.Text = tower.Description
newDescriptionTower.ScrollingFrame.Stats.CooldownText.Text = tower.Cooldown
newDescriptionTower.ScrollingFrame.Stats.DamageText.Text = tower.Damage
newDescriptionTower.ScrollingFrame.Stats.RangeText.Text = tower.Range
newDescriptionTower.ScrollingFrame.Stats.MoneyText.Text = tower.MoneyPrice
newDescriptionTower.Visible = false
newDescriptionTower.Parent = gui.MainShopFrame.ShopFrame.ShopDesc.TowerDesc.Frame
local status = getItemStatus(tower.Name)
if status == "For Sale" then
newButton.TowerImage.Price.Text = tower.Price
newButton.TowerImage.CoinIcon.Image = "rbxassetid://"
elseif status == "Equipped" then
newButton.TowerImage.Price.Text = "Equipped"
newDescriptionTower.TextButton.TextLabel.Text = "Equipped"
newButton.TowerImage.CoinIcon.Image = "rbxassetid://"
newButton.TowerImage.Price.PriceGradient.Enabled = false
newButton.TowerImage.Price.EquippedGradient.Enabled = true
else
newButton.TowerImage.Price.Text = ""
newButton.TowerImage.CoinIcon.Visible = false
end
newButton.Activated:Connect(function()
local selectedTowerFrame = nil
interactItem(tower.Name)
if newDescriptionTower.Name == newButton.Name then
newDescriptionTower.Visible = true
end
end)
end
end
local function toggleShop(player)
gui.MainShopFrame.Visible = not gui.MainShopFrame.Visible
if gui.MainShopFrame.Visible then
playerData = getDataFunc:InvokeServer()
updateItems()
end
end
local function setupShop()
local promt = Instance.new("ProximityPrompt")
promt.RequiresLineOfSight = false
promt.ActionText = "Shop"
promt.Parent = shopPromt
promt.Triggered:Connect(toggleShop)
exit.Activated:Connect(toggleShop)
menu.Activated:Connect(function()
gui.MainShopFrame.MenuFrame.Visible = not gui.MainShopFrame.MenuFrame.Visible
end)
end
setupShop()
So i assume the reason it won’t delete it is because the updateItems() keeps being used so much.
The function updateItems() calls another function called interactItem(x).
interactItem(x):
local function interactItem(itemName)
local data = interactItemFunc:InvokeServer(itemName)
if data then
playerData = data
updateItems() -- Update items called again
end
end
updateItems():
function updateItems()
StatsGui.MainFrame.Coins.TextLabel.Text = playerData.Coins
StatsGui.MainFrame.Towers.TextLabel.Text = #playerData.SelectedTowers.."/5"
for i, tower in pairs(towers) do
-- Rest of code
newButton.Activated:Connect(function()
local selectedTowerFrame = nil
interactItem(tower.Name) -- The other function is called, and that function also calls the 'updateItems' function.
if newDescriptionTower.Name == newButton.Name then
newDescriptionTower.Visible = true
end
end)
end
end
Its basically a not ending loop. therefor it keeps getting cloned. This is my theory.
I wrote some code which might work (hopefully, i wont be able to test it though)
Let me know if this works or not!
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))
local getDataFunc = ReplicatedStorage.RemoteFunctions:WaitForChild("GetData")
local interactItemFunc = ReplicatedStorage.RemoteFunctions:WaitForChild("InteractItem")
local gui = script.Parent
local exit = gui.MainShopFrame.ShopFrame.CloseButton
local menu = gui.MainShopFrame.ShopFrame.ShopMenuButton
local CoinTowerList = gui.MainShopFrame.ShopFrame.ShopDesc.BuyList.ScrollingFrame.CoinTowers.TowerList
local TowerDescFrame = gui.MainShopFrame.ShopFrame.ShopDesc.TowerDesc.Frame
local shopPromt = workspace.ShopZone.TowerShop.VenoxDevs:WaitForChild("Head")
local StatsGui = gui.StatsGui
local selectedTower = nil
local playerData = {}
local function getItemStatus(itemName)
if table.find(playerData.SelectedTowers, itemName) then
return "Equipped"
elseif table.find(playerData.OwnedTowers, itemName) then
return "Owned"
else
return "For Sale"
end
end
local function updateTowerDescription(tower)
local descriptionFrame = TowerDescFrame:FindFirstChild(tower.Name)
if not descriptionFrame then
return
end
descriptionFrame.TowerName.Text = tower.Name
descriptionFrame.ImageLabel.Image = tower.ImageAsset
descriptionFrame.ScrollingFrame.Description.StatsText.Text = tower.Description
descriptionFrame.ScrollingFrame.Stats.CooldownText.Text = tower.Cooldown
descriptionFrame.ScrollingFrame.Stats.DamageText.Text = tower.Damage
descriptionFrame.ScrollingFrame.Stats.RangeText.Text = tower.Range
descriptionFrame.ScrollingFrame.Stats.MoneyText.Text = tower.MoneyPrice
end
local function interactItem(itemName)
local data = interactItemFunc:InvokeServer(itemName)
if data then
playerData = data
updateItems()
updateTowerDescription(towers[itemName])
end
end
function updateItems()
StatsGui.MainFrame.Coins.TextLabel.Text = playerData.Coins
StatsGui.MainFrame.Towers.TextLabel.Text = #playerData.SelectedTowers.."/5"
for i, tower in pairs(towers) do
local oldButton = CoinTowerList:FindFirstChild(tower.Name)
if oldButton then
oldButton:Destroy()
end
local newButton = CoinTowerList.TowerTemplate:Clone()
newButton.Name = tower.Name
newButton.TowerName.Text = tower.Name
newButton.TowerImage.Image = tower.ImageAsset
newButton.Visible = true
newButton.LayoutOrder = tower.Price
newButton.Parent = CoinTowerList
local status = getItemStatus(tower.Name)
if status == "For Sale" then
newButton.TowerImage.Price.Text = tower.Price
newButton.TowerImage.CoinIcon.Image = "rbxassetid://"
elseif status == "Equipped" then
newButton.TowerImage.Price.Text = "Equipped"
newButton.TowerImage.CoinIcon.Image = "rbxassetid://"
newButton.TowerImage.Price.PriceGradient.Enabled = false
newButton.TowerImage.Price.EquippedGradient.Enabled = true
else
newButton.TowerImage.Price.Text = ""
newButton.TowerImage.CoinIcon.Visible = false
end
newButton.Activated:Connect(function()
interactItem(tower.Name)
if selectedTower ~= tower.Name then
updateTowerDescription(tower)
selectedTower = tower.Name
end
end)
end
end
local function toggleShop(player)
gui.MainShopFrame.Visible = not gui.MainShopFrame.Visible
if gui.MainShopFrame.Visible then
playerData = getDataFunc:InvokeServer()
updateItems()
end
end
local function setupShop()
local promt = Instance.new("ProximityPrompt")
promt.RequiresLineOfSight = false
promt.ActionText = "Shop"
promt.Parent = shopPromt
promt.Triggered:Connect(toggleShop)
exit.Activated:Connect(toggleShop)
menu.Activated:Connect(function()
gui.MainShopFrame.MenuFrame.Visible = not gui.MainShopFrame.MenuFrame.Visible
end)
end
setupShop()
Okay, I tried, it didn’t work. I see a problem that a frame with a description of the tower is simply not created when updating the store or clicking on the tower
My template is TowerDescFrame.Frame2 which was previously used for cloning
Maybe it’s because you don’t use my template anywhere.