This sounds pretty weird, but I believe it’s actually happening. When I test a game in Studio by the “Play” feature, it works fine. But then, when I test it with 2 players, the Frame gets cloned two times, for some reason.
I believe that it gets cloned by how many players are in game currently, for some reason. Aka if there were 3 players, I assume it would get cloned three times.
For more context, I’m making a spectate menu.
LocalScript:
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local RS = game:GetService("ReplicatedStorage")
local Teams = game:GetService("Teams")
local SpectateEvent = RS:WaitForChild("SpectateMenuEvents"):WaitForChild("SpectateMenu")
local DeadPlayerAssignedEvent = RS:WaitForChild("SpectateMenuEvents"):WaitForChild("DeadPlayerAssigned")
local Gui = player:WaitForChild("PlayerGui"):WaitForChild("SpectateMenu")
local HolderFrame = Gui:WaitForChild("Holder")
local SpectateButton = HolderFrame:WaitForChild("SpectateButton")
local PlayerMenu = HolderFrame:WaitForChild("PlayerMenu")
local ScrollingFrame = PlayerMenu:WaitForChild("ScrollingFrame")
local Title = HolderFrame:WaitForChild("Title")
local Spectating = HolderFrame:WaitForChild("Spectating")
local Camera = workspace.CurrentCamera
local DEBOUNCE = false
local SPECTATE_DEBOUNCE = false
local function spectateButton()
if player.Team == Teams.Dead or player.Team == Teams.Spectators then
SpectateButton.Visible = true
elseif player.Team == Teams.Alive then
SpectateButton.Visible = false
end
end
SpectateButton.MouseButton1Click:Connect(function()
if SPECTATE_DEBOUNCE == false then
PlayerMenu.Visible = true
Title.Visible = true
HolderFrame.Spectating.Visible = true
SPECTATE_DEBOUNCE = true
elseif SPECTATE_DEBOUNCE == true then
Title.Visible = false
HolderFrame.Spectating.Visible = false
PlayerMenu.Visible = false
SPECTATE_DEBOUNCE = false
end
end)
local function TeamChange(targetPlayer)
targetPlayer:GetPropertyChangedSignal("Team"):Connect(function()
SpectateEvent:FireServer(targetPlayer)
end)
end
for i,targetPlayer in pairs(Players:GetPlayers()) do
TeamChange(targetPlayer)
end
Players.PlayerAdded:Connect(TeamChange)
DeadPlayerAssignedEvent.OnClientEvent:Connect(function(targetPlayer)
local tableTemplates = ScrollingFrame:GetChildren()
for i,v in pairs(tableTemplates) do
if v.ClassName == "Frame" then
if v.Name == targetPlayer.Name then
v:Destroy()
Spectating.Text = "SPECTATING: N/A"
Camera.CameraSubject = player.Character:WaitForChild("Head")
else
v:Destroy()
end
end
end
end)
ServerScript:
local RS = game:GetService("ReplicatedStorage")
local SS = game:GetService("ServerStorage")
local PlayerTemplate = SS:WaitForChild("PlayerTemplate")
local SpectateEvent = RS:WaitForChild("SpectateMenuEvents"):WaitForChild("SpectateMenu")
local DeadPlayerAssignedEvent = RS:WaitForChild("SpectateMenuEvents"):WaitForChild("DeadPlayerAssigned")
local Teams = game:GetService("Teams")
local Players = game:GetService("Players")
local Camera = workspace.CurrentCamera
local DEBOUNCE = false
SpectateEvent.OnServerEvent:Connect(function(player, targetPlayer)
if targetPlayer.Team == Teams:FindFirstChild("Alive") then
local tablePlayers = Players:GetPlayers()
for i,v in pairs(tablePlayers) do
local targetThumbnail = Players:GetUserThumbnailAsync(targetPlayer.UserId, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size352x352)
local playerGui = v.PlayerGui:WaitForChild("SpectateMenu")
local CloneTemplateParent = playerGui:WaitForChild("Holder"):WaitForChild("PlayerMenu"):WaitForChild("ScrollingFrame")
local CloneTemplate = PlayerTemplate:Clone()
CloneTemplate.Parent = CloneTemplateParent
CloneTemplate.Name = targetPlayer.Name
CloneTemplate.PlayerName.Text = targetPlayer.Name
CloneTemplate.PlayerThumbnail.Image = targetThumbnail
CloneTemplate.PlayerName.MouseButton1Click:Connect(function()
local Spectating = CloneTemplateParent.Parent.Parent.Spectating
if DEBOUNCE == false then
DEBOUNCE = true
Spectating.Text = "SPECTATING: "..targetPlayer.Name
Camera.CameraSubject = targetPlayer.Character:WaitForChild("Head")
print("after targetPlayer CameraSubject")
elseif DEBOUNCE == true then
Spectating.Text = "SPECTATING: N/A"
Camera.CameraSubject = player.Character:WaitForChild("Head")
print("after LocalPlayer CameraSubject")
DEBOUNCE = false
end
end)
end
elseif targetPlayer.Team == Teams:FindFirstChild("Dead") or targetPlayer.Team == Teams:FindFirstChild("Spectators") then
DeadPlayerAssignedEvent:FireAllClients(targetPlayer)
end
end)
local function TeamChange(targetPlayer)
targetPlayer:GetPropertyChangedSignal("Team"):Connect(function()
SpectateEvent:FireServer(targetPlayer)
end)
end
for _, targetPlayer in pairs(Players:GetPlayers()) do
TeamChange(targetPlayer)
end
Players.PlayerAdded:Connect(function(newPlayer)
TeamChange(newPlayer)
end)
Because every time a player joins you are cloning the frame for every player currently in the game.
for i,v in pairs(tablePlayers) do
local playerGui = v.PlayerGui:WaitForChild("SpectateMenu")
local CloneTemplateParent = playerGui:WaitForChild("Holder"):WaitForChild("PlayerMenu"):WaitForChild("ScrollingFrame")
local CloneTemplate = PlayerTemplate:Clone()
I don’t fully understand what’s going on in your code, but I don’t think the for loop is necessary. You can just parent CloneTemplate to Players[targetPlayer.Name]:WaitForChild("Holder"):WaitForChild("PlayerMenu"):WaitForChild("ScrollingFrame").
Right now, your code is parenting and cloning CloneTemplate to v for i, v in pairs(tablePlayers)
This line is running a loop that iterates through every single player currently in the game and runs the code in that loop. So, once a second player joins, it will iterate through that loop again and clone another CloneTemplate gui into a player that already has it.
There’s a much more elegant solution, but a quick bandaid fix would be to just check if a player already has CloneTemplate.
local template = Players[v.Name].PlayerGui.SpectateMenu.Holder.PlayerMenu.ScrollingFrame:FindFirstChild(PlayerTemplate)
if template then continue end
This will check for PlayerTemplate inside the player being iterated through, and if it exists, it will continue and start iterating through the next player in the loop.
Not sure if it’s correct, but this is how I did it referring to your reply.
SpectateEvent.OnServerEvent:Connect(function(player, targetPlayer)
if targetPlayer.Team == Teams:FindFirstChild("Alive") then
local tablePlayers = Players:GetPlayers()
for i,v in pairs(tablePlayers) do
local template = Players[v.Name].PlayerGui.SpectateMenu.Holder.PlayerMenu.ScrollingFrame:FindFirstChildOfClass("Frame")
if template then continue end
local CloneTemplateParent = Players[v.Name].PlayerGui.SpectateMenu.Holder.PlayerMenu.ScrollingFrame
local CloneTemplate = PlayerTemplate:Clone()
end)
Nothing at all? Even the print after the if block?
SpectateEvent.OnServerEvent:Connect(function(player, targetPlayer)
if targetPlayer.Team == Teams:FindFirstChild("Alive") then
local tablePlayers = Players:GetPlayers()
for i,v in pairs(tablePlayers) do
local template = Players[v.Name].PlayerGui.SpectateMenu.Holder.PlayerMenu.ScrollingFrame:FindFirstChildOfClass("Frame")
if template then
print("Template found " ..template, v)
continue
end
print("No template found " ..template, v)
local CloneTemplateParent = Players[v.Name].PlayerGui.SpectateMenu.Holder.PlayerMenu.ScrollingFrame
local CloneTemplate = PlayerTemplate:Clone()
end)
If it isn’t printing anything then it means the loop isn’t even running…?
It looks to be working on my end. This is how I have it set up:
local RS = game:GetService("ReplicatedStorage")
local SS = game:GetService("ServerStorage")
local PlayerTemplate = SS:WaitForChild("PlayerTemplate")
local SpectateEvent = RS:WaitForChild("SpectateMenuEvents"):WaitForChild("SpectateMenu")
local DeadPlayerAssignedEvent = RS:WaitForChild("SpectateMenuEvents"):WaitForChild("DeadPlayerAssigned")
local Teams = game:GetService("Teams")
local Players = game:GetService("Players")
local Camera = workspace.CurrentCamera
local DEBOUNCE = false
SpectateEvent.OnServerEvent:Connect(function(player, targetPlayer)
if targetPlayer.Team == Teams:FindFirstChild("Alive") then
local tablePlayers = Players:GetPlayers()
for i,v in pairs(tablePlayers) do
local targetThumbnail = Players:GetUserThumbnailAsync(targetPlayer.UserId, Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size352x352)
local playerGui = v.PlayerGui:WaitForChild("SpectateMenu")
local template = Players[v.Name].PlayerGui.SpectateMenu.Holder.PlayerMenu.ScrollingFrame:FindFirstChildOfClass("Frame")
if template then
print(template, v)
continue
end
local CloneTemplateParent = playerGui:WaitForChild("Holder"):WaitForChild("PlayerMenu"):WaitForChild("ScrollingFrame")
local CloneTemplate = PlayerTemplate:Clone()
CloneTemplate.Parent = CloneTemplateParent
CloneTemplate.Name = targetPlayer.Name
CloneTemplate.PlayerName.Text = targetPlayer.Name
CloneTemplate.PlayerThumbnail.Image = targetThumbnail
CloneTemplate.PlayerName.MouseButton1Click:Connect(function()
local Spectating = CloneTemplateParent.Parent.Parent.Spectating
if DEBOUNCE == false then
DEBOUNCE = true
Spectating.Text = "SPECTATING: "..targetPlayer.Name
Camera.CameraSubject = targetPlayer.Character:WaitForChild("Head")
print("after targetPlayer CameraSubject")
elseif DEBOUNCE == true then
Spectating.Text = "SPECTATING: N/A"
Camera.CameraSubject = player.Character:WaitForChild("Head")
print("after LocalPlayer CameraSubject")
DEBOUNCE = false
end
end)
end
elseif targetPlayer.Team == Teams:FindFirstChild("Dead") or targetPlayer.Team == Teams:FindFirstChild("Spectators") then
DeadPlayerAssignedEvent:FireAllClients(targetPlayer)
end
end)
In the test server with Player2 joining after Player1:
Alright, the cloning seems to work, after I pasted in the code you just sent. However, the spectating itself doesn’t work. The camera still stays on the LocalPlayer, but the Spectating TextLabel changes to the targetPlayer.
That’s because you’re trying to access Camera on the server. You need to move that logic to a LocalScript as the CameraSubject property can only be accessed on the client