You can write your topic however you want, but you need to answer these questions:
What do you want to achieve? Keep it simple and clear!
Hello! I am designing a platformer game where the player collects rings. I used :Once() on the .Touched event for it to fire more smoothly, but now, when another player touches the ring, they can’t pick them up.
What is the issue? Include screenshots / videos if possible!
This is in a server script also. How can I have the IntValue add by 1, and not add by more than it’s supposed to.
What solutions have you tried so far? Did you look for solutions on the Developer Hub?
I have tried adding a debounce, but the same issue still occurs. I am using CollectionService for the rings with the tag ‘Ring’.
Server Script
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RemoteEvents = ReplicatedStorage:FindFirstChild("RemoteEvents")
local RingEvent = RemoteEvents:WaitForChild("RingEvent")
local CollectionService = game:GetService("CollectionService")
local Rings = CollectionService:GetTagged("Ring")
for i, ring in pairs(Rings) do
if ring:IsA("BasePart") then
ring.Touched:Once(function(Hit)
local player = Players:GetPlayerFromCharacter(Hit.Parent)
if player then
RingEvent:FireClient(player, ring)
end
end)
end
end
The rings are also not that far apart. They are in groups.
The :Once signal will (I believe) only be fired once. The moment someone touches the ring the event will be disconnected, meaning no-one else can ‘touch’ it. For this scenario I’d probably recommend sticking with just .Touched and using some other method (table?) to ensure a player can’t be rewarded twice.
Something along these lines:
for i, ring in pairs(Rings) do
if ring:IsA("BasePart") then
local ringTouched = {};
ring.Touched:Connect(function(Hit)
local player = Players:GetPlayerFromCharacter(Hit.Parent)
if player and not ringTouched[player] then
ringTouched[player] = true
RingEvent:FireClient(player, ring)
end
end)
end
end
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RemoteEvents = ReplicatedStorage:FindFirstChild("RemoteEvents")
local RingEvent = RemoteEvents:WaitForChild("RingEvent")
local CollectionService = game:GetService("CollectionService")
local Rings = CollectionService:GetTagged("Ring")
local debounceTable = {}
for i, ring in pairs(Rings) do
if ring:IsA("BasePart") then
ring.Touched:Connect(function(Hit)
local player = Players:GetPlayerFromCharacter(Hit.Parent)
if player then
if not debounceTable[ring] then -- it's saying if not true which means false then it will not run this below
debounceTable[ring] = true
RingEvent:FireClient(player, ring)
end
end
end)
end
end
So you need a table for each ring, which is why I put it just before the .Touched event. This means each ring can keep track of which players it has touched. The way you’re setting up here would mean that every time a player touched one ring, they wouldn’t be able to touch any other rings either.
By indexing a table with the player, it’s just a way to make the code look cleaner (in my opinion) and I believe once the player leaves the game it will automatically clean that entry from the table (as player == nil).
You could do this normally with just player entries in a table:
local Table = {}
table.insert(Table, player)
print(table.find(Table, player)) -- prints '1' as player is the first index in the table
print(Table)
-- prints:
-- {
-- player;
-- }
But then you’ll need to use table.find:
if not table.find(Table, player) then
-- FireClient...
end
You’re not debouncing the ring however, you’re debouncing the player. If you have another look at the code I submitted originally, it’s essentially creating a new ‘ringTouched’ table for each ring. This table will record which players touched that specific ring previously. Every time a player touches that ring, it creates a new entry in the ringTouched table that can be checked/debounced next time a player touches that ring.
Exactly, I’ll comment out my code to make it clearer:
for i, ring in pairs(Rings) do -- For every ring:
if ring:IsA("BasePart") then -- If the ring is a part:
local ringTouched = {}; -- Create a new debounce table for this specific ring
ring.Touched:Connect(function(Hit) -- When something touches the ring
local player = Players:GetPlayerFromCharacter(Hit.Parent) -- Try to get the player from it
if player and not ringTouched[player] then -- If a player exists, and that player does not exist in the ringTouched table
ringTouched[player] = true -- Add that player to the ringTouched table
RingEvent:FireClient(player, ring) -- Fire the event
end
end)
end
end
Just to clarify as well, when we talk about indexing tables you don’t have to use numerical indexes. You’re probably used to:
local Table = {
'hello',
'world'
}
Where ‘hello’ is the 1st index, and ‘world’ is the 2nd index. This can be re-written as:
local Table = {
[1] = 'hello',
[2] = 'world'
}
Which is exactly the same thing. You’d usually index this by using Table[1] → Returns ‘hello’.
However, you don’t have to use numerical indexes. Tables can be indexed with pretty much anything you want. In this case, I’m indexing it with the player instance. This limits the functionality of the table, as we’re no longer able to iterate through it numerically (as there’s no numerical order anymore), but as we don’t need to index through it numerically at any point we don’t care.
Also, semi-colons are just personal preference. You don’t need them, and the code works with-or-without. It’s purely just my personal way of doing things as I transition between languages quite a lot and many require semi-colons, so it just makes life easier for me.
for i, theRing in pairs(Rings) do
if theRing:IsA("BasePart") then
local debounceTable = {}
theRing.Touched:Connect(function(Hit)
local player = Players:GetPlayerFromCharacter(Hit.Parent)
if player then
if not debounceTable[player] then
debounceTable[player] = true
RingEvent:FireClient(player, theRing)
end
end
end)
end
end
Not quite. So think about this logically. In the first example:
Imagine you have 3 rings, each ring gets a .Touched event. The moment a player touches one of the rings, they get added to the debounce table. If that same player tried to touch another ring, they’ve already been added to the debounceTable previously, so the script won’t fire the RingEvent. I assume this is not what you want to achieve.
In the second example, a debounceTable is created for each ring, so if a player touches one of the rings, they only get added to the table for that specific ring. Then when they go to touch another ring, their name hasn’t been added to that ring’s specific debounceTable, so it will allow them to touch it.
I believe your confusion is with the idea of ‘scopes’.
Any local variables created within a function, for loop, if statement etc. is ONLY read-able by code within that ‘scope’, so within that function, for loop, if statement etc.
local variable = true;
for i=1,10 do
local variable2 = 1
print(variable) -- -> 'true'
print(variable2) -- -> '1'
end
print(variable) -- -> 'true'
print(variable2) -- -> 'Error: 'variable2' does not exist in this scope'