I need to use the touched event for certain checkpoints in my game but inside the touched event i’m firing a remote event. The issue is the touched event will run 100s of times depending on how the player walks over the checkpoint and that would cause remote event exhaustion so i need the touched event to only run once, but the event is on the server so i cant simply add a debounce otherwise, if another player touches a checkpoint within that debounce, it wont register. I was told to make a dictionary which contained debounces in it and this is what i have so far
for i, checkpoint in pairs(ends) do
local db = {}
checkpoint.Touched:Connect(function(touch)
local humanoid = touch.Parent:FindFirstChild("Humanoid")
if humanoid and humanoid.Health > 0 then
local player = Players:GetPlayerFromCharacter(touch.Parent)
db[player.UserId] = true
if player and db[player.UserId] == true then
local leaderstats = player:WaitForChild("leaderstats")
local highest_level = leaderstats.Highest_Level
local end_number = tonumber(checkpoint.Name)
if end_number - highest_level.Value == 1 then
highest_level.Value = end_number
end
endtimer:FireClient(player)
db[player.UserId] = false
end
end
end)
end
although this obviously wont work so im not sure how to go about this.
-- services
local players = game:GetService("Players")
-- variables
local debounces = {}
local object = script.Parent
-- functions
object.Touched:Connect(function(hit)
local player = players:GetPlayerFromCharacter(hit.Parent)
if (not player) then
return
end
local userId = player.UserId
if (debounces[userId]) then
return
end
-- code goes here
debounces[userId] = true
task.wait(1)
debounces[userId] = nil
end)
You get the player from the :GetPlayerFromCharacter() function using the variable hit.Parent we check if the player exists, if it does continue, and if not then return (since the player doesn’t exist) then we get the player’s userId property and if it’s in the debounce then that means the player has pressed the ‘button’ before, if not then add the player’s userId property to the debounce table and remove it after 1 second (or whatever time you want).
So everything you already have is correct, except you don’t set db[player.UserId] to false after a small duration, which makes your debounce dictionary pointless. You can simply add a task.wait for a few seconds after using :FireClient() or you can do what I do in this snippet below:
for _, checkpointPart in checkpoints do
local recentlyTouchedPlayers = {}
checkpointPart.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChildOfClass("Humanoid")
if humanoid and humanoid.Health>0 then
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
if player and recentlyTouchedPlayers[player.UserId]==nil then
recentlyTouchedPlayers[player.UserId] = true
task.delay(5, function()
recentlyTouchedPlayers[player.UserId] = nil
end)
local leaderstats = player:WaitForChild("leaderstats")
local highestLevel = leaderstats and leaderstats:FindFirstChild("Highest_Level")
if highestLevel==nil then
return
end
local highestReachedLevel = highestLevel.Value
local checkpointLevel = tonumber(checkpointPart.Name)
if checkpointLevel-highestReachedLevel==1 then
highestLevel.Value = checkpointLevel
end
endtimer:FireClient(player)
end
end
end)
end
I never had the chance to test it, but it should give an idea of what to do. I’m using a task.delay just in case the checkpoint increment code fails. Because if it did fail, the user’s id would never be removed from the dictionary.
This method of debounce works for what you want because it only affects those who touch the checkpoint. There’d be no issue if two
or more players touched the same checkpoint.
for i, checkpoint in pairs(ends) do
local db = {}
checkpoint.Touched:Connect(function(touch)
local player = Players:GetPlayerFromCharacter(touch.Parent)
local humanoid = touch.Parent:FindFirstChild("Humanoid")
if humanoid and humanoid.Health > 0 then
if player and db[player.UserId] == nil then
local leaderstats = player:WaitForChild("leaderstats")
local highest_level = leaderstats.Highest_Level
local end_number = tonumber(checkpoint.Name)
if end_number - highest_level.Value == 1 then
highest_level.Value = end_number
end
endtimer:FireClient(player)
db[player.UserId] = true
task.delay(5, function()
db[player.UserId] = nil
end)
end
end
end)
end
i modified it a little by moving the task.delay to after the other code so that wont affect anything right? also other than that it all works so thanks
I’d say it’s better to leave it where it was before because it’d be guaranteed to occur only once every five seconds per player. If the debounce related stuff occurs after, there’s the potential that it will run more than once (although the chance is very low to my knowledge). The chance of that happening is even greater/guaranteed if the leaderstat code yields or errors.