I’m currently having trouble with creating a progress bar for my Obby, I made the functionality of it updating whenever I move onto a new stage work… But I’m having trouble with Updating the progress bar whenever someone joins the game, so that it feels more polished…
One of the things I’ve tried is doing a remote event that connected to the local script from my leaderstats script, then trying to match the username of the player that just joined with the local player name, and if it matches then it will update the bar… But for some reason I couldn’t get the player’s Name from “Players.PlayerAdded:Connect(function(player)”…
I’m out of ideas, if someone could help me it would be wonderful!
Here is my current code, this is a local script:
function updateProgressBar(number)
script.Parent.MainFrame.ProgressFrame.Size = UDim2.fromScale(number, 1)
end
local Achievement = 41
local leaderstats = game.Players.LocalPlayer:WaitForChild("leaderstats")
local stageValue = leaderstats:WaitForChild("Stage")
local stageText = script.Parent.MainFrame.TextLabel
stageValue:GetPropertyChangedSignal('Value'):Connect(function()
updateProgressBar((stageValue.Value) / Achievement)
local stagePercentage = (stageValue.Value) / Achievement * 100
stageText.Text = math.round(stagePercentage) .. "%"
end)
And here is my leaderstats script, I copied it from YT but it does have a data store system that does save each player’s Stage.
local checkpoints = workspace:WaitForChild("Checkpoints")
local localPlayerJoinedEvent = game.ReplicatedStorage:WaitForChild("LocalPlayerJoined")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local DatastoreService = game:GetService("DataStoreService")
local Data = DatastoreService:GetDataStore("312374343")
local sessionData = {}
function PlayerAdded(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
local stage = Instance.new("NumberValue")
stage.Name = "Stage"
stage.Parent = leaderstats
local success = nil
local playerData = nil
local attempt = 1
repeat
success, playerData = pcall(function() -- here pcall or protected call is just repeat waiting until the data loads for the player
return Data:GetAsync(player.UserId)
end)
attempt += 1
if not success then
warn(playerData)
task.wait(2)
end
until success or attempt == 5 -- it repeats it until it loads
if success then --if it loads then make the table with their data inside
print("Data loaded: "..player.Name)
if not playerData then -- if they have no table then their a new player so we create the table
print("new player, giving default data")
playerData = {
["Stage"] = 1, --add all your values and stuff inside of the data
}
end
sessionData[player.UserId] = playerData --set the data to a table with the players id and make to make a variable
else
warn("couldnt load data: "..player.Name)
player:Kick("couldnt load your data, rejoin") --if the data couldnt load we kick them so their not just sitting there forever waiting
end
stage.Value = sessionData[player.UserId].Stage --here we get the numbervalue created above and get the value of it and set it to the value inside of the table
stage:GetPropertyChangedSignal("Value"):Connect(function()
sessionData[player.UserId].Stage = stage.Value --update the table value whenever the leaderstat value changes
end)
leaderstats.Parent = player
end
Players.PlayerAdded:Connect(function(player)
PlayerAdded(player)
player.CharacterAdded:Connect(function(char)
local leaderstats = player:WaitForChild("leaderstats")
local stage = leaderstats.Stage
local hum = char:WaitForChild("Humanoid")
task.wait()
char:MoveTo(checkpoints[stage.Value].Position)
hum.Touched:Connect(function(hit)
if hit.Parent == checkpoints then
if tonumber(hit.Name) == stage.Value + 1 then
stage.Value += 1
end
end
end)
end)
end)
function PlayerLeaving(player)
if sessionData[player.UserId] then
local success = nil
local errorMsg = nil
local attempt = 1
repeat
success, errorMsg = pcall(function()
Data:SetAsync(player.UserId, sessionData[player.UserId]) --here is the same as loading data just repeat waits until the data saves
end)
attempt += 1
if not success then
warn(errorMsg)
task.wait(2)
end
until success or attempt == 5
if success then
print("Data saved: "..player.Name)
else
warn("Cant save: "..player.Name)
end
end
end
Players.PlayerRemoving:Connect(PlayerLeaving)
function ServerShutdown()
if RunService:IsStudio() then
return
end
for i, player in ipairs(Players:GetPlayers()) do
task.spawn(function()
PlayerLeaving(player)
end)
end
end
game:BindToClose(ServerShutdown)
I’m somewhat confused, as you mentioned the progress bar after joining to load all the previous stages the player have already passed (prior to joining previously) requires datastores
Summary
to save their current stage number before the player leaves, then joining back to recover the data.
The PlayerAdded function inside the leaderstats script can be the issue with referencing the player’s name through the remote event. Is there an issue with the Client/Server replication?
When I tried referencing the PlayerAdded’s name from my leaderstats and passing through a remote event into my local script, I couldn’t get the new player’s name for some reason.
Which means that I couldn’t match it with the local player’s name to update the progress bar
I’m assuming you’re trying to connect a remote event from server to client by doing something like :FireClient(player, stage.Value). Don’t know if it’s just me, but I don’t see that anywhere in the server script.
and then on the progress bar local script I think I did:
PlayerJoinedEvent.OnClientEvent:Connect(function(player)
local playerName = player.Name
if playerName == game.Players.LocalPlayer then
updates progress bar**
end)
But this didn’t work, there was an error with the player that I passed through the remote event and I couldn’t use the name. So I deleted it loll.
If you want me to replicate this problem then I am more than happy to! Thanks for helping me out!
Everything from the server script seemed fine to me for the most part. Do you remember what error showed up when you printed the player’s name? Try creating the same remote events but including the leaderstats.Stage.Value with player into one of the parameters when you fire the client.
Here’s the problem. When you run :FireClient(player), the .OnClientEvent connection does not take player as a parameter.
:FireClient() takes player as an argument to know what player to send the event to. .OnClientEvent ignores the player argument so what really happens is no data was passed over, the player argument is only there to specify what player takes the event.
PlayerJoinedEvent.OnClientEvent:Once(function()
--you don't need to compare usernames since this was already specified in the FireClient call
--update progress bar
end)
If you want, here are some :FireClient() and .OnClientEvent connection examples:
exampleEvent:FireClient(examplePlayer, "HELLO", 112301)
exampleEvent.OnClientEvent:Connect(function(param1, param2)
print(param1, param2) --> "HELLO 112301"
end)
exampleEvent2:FireClient(anotherPlayer)
exampleEvent2.OnClientEvent:Connect(function(param1, param2, param3, param4)
print(param1, param2, param3, param4) --> "nil nil nil nil" (because no data was passed)
end)
exampleEvent3:FireClient("hello") --> errors because the first argument must be a player
Server Script (the leaderstats and stagevalue portion), my event is called LocalPlayerJoined, and I put the FireClient() at the bottom of this script.:
local checkpoints = workspace:WaitForChild("Checkpoints")
local localPlayerJoinedEvent = game.ReplicatedStorage:WaitForChild("LocalPlayerJoined")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local DatastoreService = game:GetService("DataStoreService")
local Data = DatastoreService:GetDataStore("312374343")
local sessionData = {}
local LocalPlayerJoinedEvent = game.ReplicatedStorage.LocalPlayerJoined
function PlayerAdded(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
local stage = Instance.new("NumberValue")
stage.Name = "Stage"
stage.Parent = leaderstats
local success = nil
local playerData = nil
local attempt = 1
repeat
success, playerData = pcall(function() -- here pcall or protected call is just repeat waiting until the data loads for the player
return Data:GetAsync(player.UserId)
end)
attempt += 1
if not success then
warn(playerData)
task.wait(2)
end
until success or attempt == 5 -- it repeats it until it loads
if success then --if it loads then make the table with their data inside
print("Data loaded: "..player.Name)
if not playerData then -- if they have no table then their a new player so we create the table
print("new player, giving default data")
playerData = {
["Stage"] = 1, --add all your values and stuff inside of the data
}
end
sessionData[player.UserId] = playerData --set the data to a table with the players id and make to make a variable
else
warn("couldnt load data: "..player.Name)
player:Kick("couldnt load your data, rejoin") --if the data couldnt load we kick them so their not just sitting there forever waiting
end
stage.Value = sessionData[player.UserId].Stage --here we get the numbervalue created above and get the value of it and set it to the value inside of the table
stage:GetPropertyChangedSignal("Value"):Connect(function()
sessionData[player.UserId].Stage = stage.Value --update the table value whenever the leaderstat value changes
end)
leaderstats.Parent = player
PlayerJoinedEvent:FireClient(player, stage.Value)
end
And then for the Local Script of the progress bar:
local LocalPlayerJoinedEvent = game.ReplicatedStorage:WaitForChild("LocalPlayerJoined")
local Achievement = 41
local leaderstats = game.Players.LocalPlayer:WaitForChild("leaderstats")
local stageValue = leaderstats:WaitForChild("Stage")
local stageText = script.Parent.MainFrame.TextLabel
function updateProgressBar(number)
script.Parent.MainFrame.ProgressFrame.Size = UDim2.fromScale(number, 1)
end
stageValue:GetPropertyChangedSignal('Value'):Connect(function()
updateProgressBar((stageValue.Value) / Achievement)
local stagePercentage = (stageValue.Value) / Achievement * 100
stageText.Text = math.round(stagePercentage) .. "%"
end)
playerJoinedEvent.OnClientEvent:Connect(function(player, stageNum)
if player.Name == game.Players.LocalPlayer.Name then
updateProgressBar((stageNum) / Achievement)
end
end)
So when I try to play the game, I immediately get the error "Players.dogpoopgamenocap.PlayerGui.ProgressBar.LocalScript:20: attempt to index nil with ‘OnClientEvent’ "
The function should be .OnClientEvent:Connect(function(stageNum) and the if statement below the function isn’t needed since both the playerAdded function and the :FireClient(player) has already provided the player by default