So I am practicing my scripting skills by trying to make an obby. I am trying to make it so when you step on a part it adds +1 to your leaderstats, I found out how that works but my problem is that it gives alot because I am using a IV Pairs loop. Can anyone tell me if there is some sort of way that I will be able to only add 1 while still using a IV pairs loop?
-- This is an example Lua code block
local DataStoreService = game:GetService("DataStoreService")
local MyDataStore = DataStoreService:GetDataStore("MyDataStore") --If we change the name it makes a whole new datastore
--:Code below: Gets a players data when they join
game.Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder")--:All this script makes a folder for all the data to be in
leaderstats.Name = "leaderstats" --:Sets the name of the folder
leaderstats.Parent = player --:Sets the parent of the folder the player
local Stage = Instance.new("IntValue")--:Makes a value for cash in the leaderboard
Stage.Name = "Stage" --:Names the cash
Stage.Parent = leaderstats --:Gives the cash a parent which is the datastore folder
--local Cash = Instance.new("IntValue")
--Cash.Name = "Cash"
--Cash.Parent = leaderstats
local playerUserId = "Player_"..player.UserId --Saves the players Id so it keeps his data after he leave
local data
local success, errormessage = pcall(function() --Finds out if a player has played the game
data = MyDataStore:GetAsync(playerUserId) --If they have it gets their data
end)
if success then
Stage.Value = data.Stage
--Wins.Value = data.Wins
--Sets our data equal to the current cash
end
local CheckPoints = game.Workspace:FindFirstChild("CheckPoints")
for i, CheckPoints in pairs(CheckPoints:GetChildren()) do
CheckPoints.Touched:Connect(function(hit)
player.leaderstats.Stage.Value = player.leaderstats.Stage.Value + 1
end)
end
end)
--Code below: Saves the players data when they leave
game.Players.PlayerRemoving:Connect(function(player) --You need player so we can find out who is leaving
local playerUserId = "Player_"..player.UserId
local data = {
Stage = player.leaderstats.Stage.Value;
--Wins = player.leaderstats.Cash.Value;
}
local success, errormessage = pcall(function() --Makes a pcall to find out if it was success or error
MyDataStore:SetAsync(playerUserId, data) --Saves the data to the PlayerId when they leave
end)
if success then
print("Data Successfully saved")
else
print("There was an error when saving data")
warn(errormessage)
end
end)
This is not actually because you’re using a for i,v in pairs() loop. Using this type of loop is essentially just listening for a Touched event on every child of the CheckPoints folder. Your problem is that the checkpoints are being touched more than once, meaning the Touched event is being fired multiple times as a result.
You will need to add a check whenever a player touches a checkpoint that they haven’t already touched the stage. You will need to give each checkpoint a unique name, and I recommend numbering them to make it easier.
For example, your CheckPoints folder may look like this:
You could also name each one “Stage1”, “Stage2”, etc, but you would have to split the name string later in the code.
Your code will look something like this:
---> services
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local MyDataStore = DataStoreService:GetDataStore("MyDataStore")
-- get a player's data when they join
Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local Stage = Instance.new("IntValue")
Stage.Name = "Stage"
Stage.Parent = leaderstats
local playerUserId = "Player_"..player.UserId
local data
local success,errorMessage = pcall(function()
data = MyDataStore:GetAsync(playerUserId)
end)
if success then
Stage.Value = data.Stage
end
end)
-- checkpoints
local CheckPoints = game.Workspace:WaitForChild("CheckPoints")
for i,checkpoint in pairs(CheckPoints:GetChildren()) do
checkpoint.Touched:Connect(function(hit)
if hit.Parent and hit.Parent:FindFirstChildOfClass("Humanoid") then
local player = Players:GetPlayerFromCharacter(hit.Parent)
if player then
if player.leaderstats.Stage.Value == (tonumber(checkpoint.Name) - 1) then
player.leaderstats.Stage.Value += 1
end
end
end
end)
end
-- save the player's data when they leave
game.Players.PlayerRemoving:Connect(function(player) --You need player so we can find out who is leaving
local playerUserId = "Player_"..player.UserId
local data = {
Stage = player.leaderstats.Stage.Value;
--Wins = player.leaderstats.Cash.Value;
}
local success, errormessage = pcall(function() --Makes a pcall to find out if it was success or error
MyDataStore:SetAsync(playerUserId, data) --Saves the data to the PlayerId when they leave
end)
if success then
print("Data Successfully saved")
else
print("There was an error when saving data")
warn(errormessage)
end
end)
I also do not recommend doing the for loop every single time a player joins, so the function is removed from the function and a new check to see if a player is touching it is above.
Also, understand why we check that the current value of the stage is 1 less than the checkpoint they are touching - it should prevent people being able to exploit right to the end and get stages, as their current stage is not 1 less than the stage they are touching.
You can create a block list table in which you insert a player when it touches the part. Then, use TouchEnded event to remove the player from block list.
I am using the excact script only just that I added game.Players at line 37 in so it wouldn’t give me an error. This is line 37: local player = game.Players:GetPlayerFromCharacter(hit.Parent)
Also this is line 39: if player.leaderstats.Stage.Value == (tonumber(checkpoint.Name) - 1) then