I have these parts called “rails” and I am creating a script to detect if a player is touching the rail at all. There is a boolean value inserted into every player called "IsTouchingRail". It is “hooked up” to both a .Touched and .TouchEnded event. When a body part touches the rail, it adds the part to a table, and when the touch has ended, it removes the body part from the table. When the table is empty, it sets the IsTouchingRail value to false.
The problem
This system works fine when you are just standing still on the "rail’ (part) but not when you are walking on it doesn’t. Here’s why: when your walking on a part, you lift up your foot the .TouchEnded event is fired and removes the part from the table, then it sees that you put there is nothing in the table, so it sets the value to false. At the same time, your other foot touches the part, but the .Touched event does not fire fast enough to set it back to true. This little delay is a problem because when your touching the rail, it’s supposed disable the ability to turn on your jetpack but because of this delay, you can turn on your jetpack.
Code
for i, rail in pairs(rails:GetChildren()) do
rail.Touched:Connect(function(part)
local player = game.Players:GetPlayerFromCharacter(part.Parent) --Verifies if the body part belongs to a player
if player ~= nil and table.find(bodyPartTouching[player.Name],part) == nil then --Checks if player was found and makes sure index doesn't already
--exist
table.insert(bodyPartTouching[player.Name],1,part) --Inserts part into table
if player.IsTouchingRail.Value == false then
print("Player is now touching")
player.IsTouchingRail.Value = true --Sets value
end
end
end)
rail.TouchEnded:Connect(function(part)
local player = game.Players:GetPlayerFromCharacter(part.Parent)
if player ~= nil then
table.remove(bodyPartTouching[player.Name], table.find(bodyPartTouching[player.Name],part)) --Removes it from table
if bodyPartTouching[player.Name][1] == nil then --Checks if table is empty
player.IsTouchingRail.Value = false --Sets Value
end
end
end)
end
My solution and question
As a solution to this problem I have decided that every part detected by .Touched should be needs to be debounced (for about 1/2 second) so the .TouchEnded can’t detect it too quickly. Now the question: What would be the best method of individually debouncing parts?
Should I use tick() and store the time the part was touched in a table?
My module might help you out? I am not really following with your code at the moment.
I originally wrote it to simplify teleport queues, but it’s useful in other scenarios. If you mask your “rails” with a hit-box, you can use my module to query for when a player enters/leaves said hit-box.
Here’s an example application:
-- Services
local ServerStorage = game:GetService("ServerStorage")
-- Variables
local PlayerTracker = require(ServerStorage.PlayerTracker)
local Tracker = PlayerTracker.new(--[[Path.to.BasePart]])
-- Functions
local function OnPlayerEntered(Player)
-- ...
end
local function OnPlayerLeft(Player)
-- ...
end
-- Setup
Tracker.PlayerEntered:Connect(OnPlayerEntered)
Tracker.PlayerLeft:Connect(OnPlayerLeft)
Tracker:StartTracking()
I don’t think that’s the issue. I did some testing and here’s what I found:
Test script
local rails = game.Workspace.Rails
local bodyPartTouching = {}
local touching = {}
game.Players.PlayerAdded:Connect(function(player)
bodyPartTouching[player] = {}
touching[player] = false
end)
for i, rail in pairs(rails:GetChildren()) do
rail.Touched:Connect(function(part)
local player = game.Players:GetPlayerFromCharacter(part.Parent) --Verifies if the body part belongs to a player
if not player then return end
if not table.find(bodyPartTouching[player], part) then --Checks if player was found and makes sure index doesn't already exist
table.insert(bodyPartTouching[player], part) --Inserts part into table
touching[player] = #bodyPartTouching[player] > 0
--print("Touch", part, #bodyPartTouching[player])
end
end)
rail.TouchEnded:Connect(function(part)
local player = game.Players:GetPlayerFromCharacter(part.Parent)
if not player then return end
if part.Name == "HumanoidRootPart" then
print("Huh?!")
end
table.remove(bodyPartTouching[player], table.find(bodyPartTouching[player], part)) --Removes it from table
touching[player] = #bodyPartTouching[player] > 0
if #bodyPartTouching[player] == 0 then --Checks if table is empty
print("Completely ended")
end
end)
end
The HumanoidRootPart pretty much never fires the TouchEnded event, but usually fires the Touched event. This means it gets added to the list of touching limbs but never gets removed. You can verify this for yourself using the test script.
Here’s a version that just ignores that part:
Summary
local rails = game.Workspace.Rails
local bodyPartTouching = {}
local touching = {}
game.Players.PlayerAdded:Connect(function(player)
bodyPartTouching[player] = {}
touching[player] = false
end)
for i, rail in pairs(rails:GetChildren()) do
rail.Touched:Connect(function(part)
if part.Name == "HumanoidRootPart" then return end
local player = game.Players:GetPlayerFromCharacter(part.Parent) --Verifies if the body part belongs to a player
if not player then return end
if not table.find(bodyPartTouching[player], part) then --Checks if player was found and makes sure index doesn't already exist
table.insert(bodyPartTouching[player], part) --Inserts part into table
local wasTouching = touching[player]
touching[player] = #bodyPartTouching[player] > 0
if touching[player] and not wasTouching then
print("Began touching")
end
end
end)
rail.TouchEnded:Connect(function(part)
local player = game.Players:GetPlayerFromCharacter(part.Parent)
if not player then return end
table.remove(bodyPartTouching[player], table.find(bodyPartTouching[player], part)) --Removes it from table
touching[player] = #bodyPartTouching[player] > 0
if #bodyPartTouching[player] == 0 then --Checks if table is empty
print("Touch completely ended")
end
end)
end
With that it alternates between touching and not touching, which is the expected behavior:
...
Touch completely ended
Began touching
Touch completely ended
Began touching
Touch completely ended
Began touching
It’s way too sensitive though, just walking on a surface causes it to switch several times per second. This is because the default walking animation causes the feet to actually start and stop touching the ground. T he way I’d fix it is with an additional “detector part” for detecting if the player is standing on a surface:
It’s welded to the character so that it juuuust sticks into the ground.
Thank you! However, I’m not sure if you know this, but this is about when the player touched a part, not when the player enters a hitbox (Sorry if this comes across as a little rude, I have no intentions). But also can this work for a problem regarding touching a part? If so, can you clarify and provide some explanation regarding the methods you used and how they work?
By overlay, do you mean a part that is only slightly bigger than the original part. And can you please go into detail about how your methods that you use work?
The general idea is to study the limbs of characters present within the defined space. If a specific set of limbs no longer appear in the next study, we know that the relative character has left said defined space.