So the scripts below are part of a trail system I have in my game the only problem is when the player first spawns in and clicks on the equip button the trail does not attach until they die. After that the script works fine. What is the issue and how can I fix it?
First script
local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("DATA")
local trails = game.ReplicatedStorage:WaitForChild("Trails")
function saveData(plrLeaving)
local ownedTrails = {}
for i, trail in pairs(plrLeaving.OwnedTrails:GetChildren()) do
table.insert(ownedTrails, trail.Name)
end
local success, err = pcall(function()
ds:SetAsync("trails-" .. plrLeaving.UserId, ownedTrails)
ds:SetAsync("Coins-" .. plrLeaving.UserId, plrLeaving.leaderstats.Coins.Value)
end)
end
game.Players.PlayerAdded:Connect(function(plr)
local trailsOwned = {}
pcall(function()
trailsOwned = ds:GetAsync("trails-" .. plr.UserId) or {}
end)
local ownedFolder = Instance.new("Folder")
ownerFolder.Parent = plr
ownedFolder.Name = "OwnedTrails"
for i, owned in pairs(trailsOwned) do
if trails:FindFirstChild(owned) then
trails[owned]:Clone().Parent = ownedFolder
end
end
plr.CharacterAdded:Connect(function(char)
local hrp = char:WaitForChild("HumanoidRootPart")
local atchTop = Instance.new("Attachment")
atchTop.Parent = hrp
atchTop.Name = "TrailTop"
atchTop.Position = Vector3.new(0, 0.766, 0)
local atchBtm = Instance.new("Attachment")
atchBtm.Parent = hrp
atchBtm.Name = "TrailBottom"
atchBtm.Position = Vector3.new(0, -0.766, 0)
if plr:FindFirstChild("LastTrail") then
local newTrail = plr:FindFirstChild("LastTrail").Value:Clone()
newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
newTrail.Parent = char.HumanoidRootPart
end
end)
end)
game.Players.PlayerRemoving:Connect(saveData)
game:BindToClose(function()
for i, plrLeaving in pairs(game.Players:GetPlayers()) do
saveData(plrLeaving)
end
end)
game.ReplicatedStorage.TrailSelectedRE.OnServerEvent:Connect(function(plr, buying, trail)
if buying and not plr.OwnedTrails:FindFirstChild(trail.Name) then
local price = trail.Price.Value
local coins = plr.leaderstats.Coins
if price <= coins.Value then
coins.Value -= price
trail:Clone().Parent = plr.OwnedTrails
local char = plr.Character
if not char or not char:FindFirstChild("HumanoidRootPart") then return end
for i, child in pairs(char.HumanoidRootPart:GetChildren()) do
if child:IsA("Trail") then child:Destroy() end
end
local newTrail = trail:Clone()
newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
newTrail.Parent = char.HumanoidRootPart
end
elseif not buying and plr.OwnedTrails:FindFirstChild(trail.Name) then
if plr:FindFirstChild("LastTrail") then
plr:FindFirstChild("LastTrail").Value = trail
else
local obj = Instance.new("ObjectValue")
obj.Parent = plr
obj.Name = "LastTrail"
obj.Value = trail
end
local char = plr.Character
if not char or not char:FindFirstChild("HumanoidRootPart") then return end
for i, child in pairs(char.HumanoidRootPart:GetChildren()) do
if child:IsA("Trail") then child:Destroy() end
end
local newTrail = trail:Clone()
newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
newTrail.Parent = char.HumanoidRootPart
end
end)
Second script
local shopFrame = script.Parent:WaitForChild(“ShopFrame”)
local invFrame = script.Parent:WaitForChild(“InventoryFrame”)
local trailsFolder = game.ReplicatedStorage:WaitForChild(“Trails”)
local ownedTrailsFolder = game.Players.LocalPlayer:WaitForChild(“OwnedTrails”)
function updateInventory()
for i, child in pairs(invFrame.TrailsScroller:GetChildren()) do
if child:IsA("ImageLabel") then child:Destroy() end
end
local ownedTrails = ownedTrailsFolder:GetChildren()
table.sort(ownedTrails, function(a, b)
return trailsFolder[a.Name].Price.Value < trailsFolder[b.Name].Price.Value or trailsFolder[a.Name].Price.Value == trailsFolder[b.Name].Price.Value and a.Name < b.Name
end)
for i, trail in pairs(ownedTrails) do
local item = script.Item:Clone()
item.SelectButton.Text = "EQUIP"
item.TrailName.Text = trail.Name
item.Trail.UIGradient.Color = trail.Color
item.Parent = invFrame.TrailsScroller
if game.Players.LocalPlayer.Character:WaitForChild("HumanoidRootPart"):FindFirstChild(trail.Name) then
item.SelectButton.Text = "EQUIPPED"
end
item.SelectButton.MouseButton1Click:Connect(function()
game.ReplicatedStorage.TrailSelectedRE:FireServer(false, trail)
for i, itemButton in pairs(item.Parent:GetChildren()) do
if itemButton:IsA("ImageLabel") then
itemButton.SelectButton.Text = "EQUIP"
end
end
item.SelectButton.Text = "EQUIPPED"
end)
end
end
function updateShop()
for i, child in pairs(shopFrame.Frame1Effects.TrailsScroller:GetChildren()) do
if child:IsA("ImageLabel") then child:Destroy() end
end
local shopTrails = trailsFolder:GetChildren()
table.sort(shopTrails, function(a, b)
return a.Price.Value < b.Price.Value or a.Price.Value == b.Price.Value and a.Name < b.Name
end)
for i, trail in pairs(shopTrails) do
local item = script.Item:Clone()
item.SelectButton.Text = "BUY for " .. trail.Price.Value
if ownedTrailsFolder:FindFirstChild(trail.Name) then item.SelectButton.Text = "Owned." end
item.TrailName.Text = trail.Name
item.Trail.UIGradient.Color = trail.Color
item.Parent = shopFrame.Frame1Effects.TrailsScroller
item.SelectButton.MouseButton1Click:Connect(function()
game.ReplicatedStorage.TrailSelectedRE:FireServer(true, trail)
end)
end
end
updateShop()
updateInventory()
ownedTrailsFolder.ChildAdded:Connect(function()
updateInventory(); updateShop()
end)
ownedTrailsFolder.ChildRemoved:Connect(function()
updateInventory(); updateShop()
end)
trailsFolder.ChildAdded:Connect(updateShop)
trailsFolder.ChildRemoved:Connect(updateShop)
You have yielding code in your PlayerAdded function which will push back the time that the CharacterAdded connection is created. By the time the connection is made the player may already have a character. This is not an error, this is you not accounting for something that may potentially exist.
The problem is the same for your PlayerAdded function by the way. You may not have encountered issues with it right now but in the future you may or will. You always need to handle objects that may already exist by the time your connections are established.
PlayerAdded can be resolved by putting the code in a function that you can both connect to and run on all existing players:
local Players = game:GetService("Players")
local function playerAdded(player)
end
Players.PlayerAdded:Connect(playerAdded)
for _, player in ipairs(Players:GetPlayers()) do
playerAdded(player)
end
The character code can be done in the same manner:
local function playerAdded(player)
local function characterAdded(character)
end
player.CharacterAdded:Connect(characterAdded)
if player.Character then
characterAdded(player.Character)
end
end
local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("DATA")
local rs = game:GetService("ReplicatedStorage")
local trails = rs:WaitForChild("Trails")
local selectedRemote = rs:WaitForChild("TrailSelectedRE")
local players = game:GetService("Players")
players.PlayerAdded:Connect(function(player)
local trailsOwned = nil
local ownedFolder = Instance.new("Folder")
ownedFolder.Parent = player
ownedFolder.Name = "OwnedTrails"
task.spawn(function()
pcall(function()
trailsOwned = ds:GetAsync("trails-" .. player.UserId) or {}
end)
end)
player.CharacterAdded:Connect(function(char)
repeat task.wait()
until type(trailsOwned) == "table"
for _, owned in ipairs(trailsOwned) do
if trails:FindFirstChild(owned) then
trails[owned]:Clone().Parent = ownedFolder
end
end
local hrp = char:WaitForChild("HumanoidRootPart")
local atchTop = Instance.new("Attachment")
atchTop.Parent = hrp
atchTop.Name = "TrailTop"
atchTop.Position = Vector3.new(0, 0.766, 0)
local atchBtm = Instance.new("Attachment")
atchBtm.Parent = hrp
atchBtm.Name = "TrailBottom"
atchBtm.Position = Vector3.new(0, -0.766, 0)
if player:FindFirstChild("LastTrail") then
local newTrail = player:FindFirstChild("LastTrail").Value:Clone()
newTrail.Attachment0 = hrp.TrailTop
newTrail.Attachment1 = hrp.TrailBottom
newTrail.Parent = hrp
end
end)
end)
players.PlayerRemoving:Connect(function(player)
local ownedTrails = {}
for _, trail in ipairs(player.OwnedTrails:GetChildren()) do
table.insert(ownedTrails, trail.Name)
end
local success1, err1 = pcall(function()
ds:SetAsync("trails-" .. player.UserId, ownedTrails)
end)
local success2, err2 = pcall(function()
ds:SetAsync("Coins-" .. player.UserId, player.leaderstats.Coins.Value)
end)
end)
game:BindToClose(function()
for _, player in ipairs(players:GetPlayers()) do
local ownedTrails = {}
for _, trail in ipairs(player.OwnedTrails:GetChildren()) do
table.insert(ownedTrails, trail.Name)
end
local success1, err1 = pcall(function()
ds:SetAsync("trails-" .. player.UserId, ownedTrails)
end)
local success2, err2 = pcall(function()
ds:SetAsync("Coins-" .. player.UserId, player.leaderstats.Coins.Value)
end)
end
end)
selectedRemote.OnServerEvent:Connect(function(plr, buying, trail)
if buying and not plr.OwnedTrails:FindFirstChild(trail.Name) then
local price = trail.Price.Value
local coins = plr.leaderstats.Coins
if price <= coins.Value then
coins.Value -= price
local trailClone = trail:Clone()
trailClone.Parent = plr.OwnedTrails
local char = plr.Character
if not char or not char:FindFirstChild("HumanoidRootPart") then
return
end
local hrp = char:WaitForChild("HumanoidRootPart")
for _, child in ipairs(hrp:GetChildren()) do
if child:IsA("Trail") then child:Destroy() end
end
local newTrail = trail:Clone()
newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
newTrail.Parent = char.HumanoidRootPart
end
elseif not buying and plr.OwnedTrails:FindFirstChild(trail.Name) then
if plr:FindFirstChild("LastTrail") then
plr:FindFirstChild("LastTrail").Value = trail
else
local obj = Instance.new("ObjectValue")
obj.Parent = plr
obj.Name = "LastTrail"
obj.Value = trail
end
local char = plr.Character
if not char or not char:FindFirstChild("HumanoidRootPart") then
return
end
local hrp = char:WaitForChild("HumanoidRootPart")
for _, child in ipairs(hrp:GetChildren()) do
if child:IsA("Trail") then child:Destroy() end
end
local newTrail = trail:Clone()
newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
newTrail.Parent = char.HumanoidRootPart
end
end)
I think the main issue was the following typo.
local ownedFolder = Instance.new("Folder")
ownerFolder.Parent = plr --ownerFolder instead of ownedFolder here
ownedFolder.Name = "OwnedTrails"
Although I made some additional revisions and general cleaning up.
local gui = script.Parent
local shopFrame = gui:WaitForChild("ShopFrame")
local frameEffects = shopFrame:WaitForChild("Frame1Effects")
local trailsScroller2 = frameEffects:WaitForChild("TrailsScroller")
local invFrame = gui:WaitForChild("InventoryFrame")
local trailsScroller = invFrame:WaitForChild("TrailsScroller")
local players = game:GetService("Players")
local player = players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local hrp = character:WaitForChild("HumanoidRootPart")
local ownedTrailsFolder = player:WaitForChild("OwnedTrails")
local rs = game:GetService("ReplicatedStorage")
local trailsFolder = rs:WaitForChild("Trails")
local selectedRemote = rs:WaitForChild("TrailSelectedRE")
local item = script:WaitForChild("Item")
function updateInventory()
for _, child in ipairs(trailsScroller:GetChildren()) do
if child:IsA("ImageLabel") then
child:Destroy()
end
end
local ownedTrails = ownedTrailsFolder:GetChildren()
table.sort(ownedTrails, function(a, b)
return trailsFolder[a.Name].Price.Value < trailsFolder[b.Name].Price.Value or trailsFolder[a.Name].Price.Value == trailsFolder[b.Name].Price.Value and a.Name < b.Name
end)
for _, trail in ipairs(ownedTrails) do
local item = item:Clone()
item.SelectButton.Text = "EQUIP"
item.TrailName.Text = trail.Name
item.Trail.UIGradient.Color = trail.Color
item.Parent = invFrame.TrailsScroller
if hrp:FindFirstChild(trail.Name) then
item.SelectButton.Text = "EQUIPPED"
end
item.SelectButton.MouseButton1Click:Connect(function()
selectedRemote:FireServer(false, trail)
for _, itemButton in ipairs(trailsScroller:GetChildren()) do
if itemButton:IsA("ImageLabel") then
itemButton.SelectButton.Text = "EQUIP"
end
end
item.SelectButton.Text = "EQUIPPED"
end)
end
end
function updateShop()
for _, child in ipairs(trailsScroller2:GetChildren()) do
if child:IsA("ImageLabel") then
child:Destroy()
end
end
local shopTrails = trailsFolder:GetChildren()
table.sort(shopTrails, function(a, b)
return a.Price.Value < b.Price.Value or a.Price.Value == b.Price.Value and a.Name < b.Name
end)
for _, trail in ipairs(shopTrails) do
local item = item:Clone()
item.SelectButton.Text = "BUY for " .. trail.Price.Value
if ownedTrailsFolder:FindFirstChild(trail.Name) then
item.SelectButton.Text = "Owned."
end
item.TrailName.Text = trail.Name
item.Trail.UIGradient.Color = trail.Color
item.Parent = shopFrame.Frame1Effects.TrailsScroller
item.SelectButton.MouseButton1Click:Connect(function()
selectedRemote:FireServer(true, trail)
end)
end
end
updateShop()
updateInventory()
ownedTrailsFolder.ChildAdded:Connect(function()
updateInventory(); updateShop()
end)
ownedTrailsFolder.ChildRemoved:Connect(function()
updateInventory(); updateShop()
end)
trailsFolder.ChildAdded:Connect(updateShop)
trailsFolder.ChildRemoved:Connect(updateShop)
local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("DATA")
local rs = game:GetService("ReplicatedStorage")
local trails = rs:WaitForChild("Trails")
local selectedRemote = rs:WaitForChild("TrailSelectedRE")
local players = game:GetService("Players")
players.PlayerAdded:Connect(function(player)
local trailsOwned = nil
local ownedFolder = Instance.new("Folder")
ownedFolder.Parent = player
ownedFolder.Name = "OwnedTrails"
task.spawn(function()
pcall(function()
trailsOwned = ds:GetAsync("trails-" .. player.UserId) or {}
end)
repeat task.wait()
until type(trailsOwned) == "table"
for _, owned in ipairs(trailsOwned) do
if trails:FindFirstChild(owned) then
trails[owned]:Clone().Parent = ownedFolder
end
end
end)
player.CharacterAdded:Connect(function(char)
local hrp = char:WaitForChild("HumanoidRootPart")
local atchTop = Instance.new("Attachment")
atchTop.Parent = hrp
atchTop.Name = "TrailTop"
atchTop.Position = Vector3.new(0, 0.766, 0)
local atchBtm = Instance.new("Attachment")
atchBtm.Parent = hrp
atchBtm.Name = "TrailBottom"
atchBtm.Position = Vector3.new(0, -0.766, 0)
if player:FindFirstChild("LastTrail") then
local newTrail = player:FindFirstChild("LastTrail").Value:Clone()
newTrail.Attachment0 = hrp.TrailTop
newTrail.Attachment1 = hrp.TrailBottom
newTrail.Parent = hrp
end
end)
end)
players.PlayerRemoving:Connect(function(player)
local ownedTrails = {}
for _, trail in ipairs(player.OwnedTrails:GetChildren()) do
table.insert(ownedTrails, trail.Name)
end
local success1, err1 = pcall(function()
ds:SetAsync("trails-" .. player.UserId, ownedTrails)
end)
local success2, err2 = pcall(function()
ds:SetAsync("Coins-" .. player.UserId, player.leaderstats.Coins.Value)
end)
end)
game:BindToClose(function()
for _, player in ipairs(players:GetPlayers()) do
local ownedTrails = {}
for _, trail in ipairs(player.OwnedTrails:GetChildren()) do
table.insert(ownedTrails, trail.Name)
end
local success1, err1 = pcall(function()
ds:SetAsync("trails-" .. player.UserId, ownedTrails)
end)
local success2, err2 = pcall(function()
ds:SetAsync("Coins-" .. player.UserId, player.leaderstats.Coins.Value)
end)
end
end)
selectedRemote.OnServerEvent:Connect(function(plr, buying, trail)
if buying and not plr.OwnedTrails:FindFirstChild(trail.Name) then
local price = trail.Price.Value
local coins = plr.leaderstats.Coins
if price <= coins.Value then
coins.Value -= price
local trailClone = trail:Clone()
trailClone.Parent = plr.OwnedTrails
local char = plr.Character
if not char or not char:FindFirstChild("HumanoidRootPart") then
return
end
local hrp = char:WaitForChild("HumanoidRootPart")
for _, child in ipairs(hrp:GetChildren()) do
if child:IsA("Trail") then child:Destroy() end
end
local newTrail = trail:Clone()
newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
newTrail.Parent = char.HumanoidRootPart
end
elseif not buying and plr.OwnedTrails:FindFirstChild(trail.Name) then
if plr:FindFirstChild("LastTrail") then
plr:FindFirstChild("LastTrail").Value = trail
else
local obj = Instance.new("ObjectValue")
obj.Parent = plr
obj.Name = "LastTrail"
obj.Value = trail
end
local char = plr.Character
if not char or not char:FindFirstChild("HumanoidRootPart") then
return
end
local hrp = char:WaitForChild("HumanoidRootPart")
for _, child in ipairs(hrp:GetChildren()) do
if child:IsA("Trail") then child:Destroy() end
end
local newTrail = trail:Clone()
newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
newTrail.Parent = char.HumanoidRootPart
end
end)
It works no more duping thanks homie. Quick question though is there a way for me to get rid of the dupped trails in my personal inventory or do I have to create a delete button gui?