How to consistently check if player is touching a part

The problem I am facing is if you touch the part, it will go “Players: 1 / 8” but if you move, while still inside the part, it will drop to 0 /8, then back to 1 /8. This is due the TouchEnded firing (as a part of the body is obviously moving)

However I want it to stay 1 / 8 until my player actually leaves the area. I was thinking of Region3, however I want to use a circle area, and not a square based area.

-- Player has touched the 'Play' area
local function PlayTouched(hit)
	local Character = hit.Parent
	if not Character then return end
	
	local Player = Players:GetPlayerFromCharacter(Character)
	if not Player then
		Character = hit.Parent.Parent
		Player = Players:GetPlayerFromCharacter(Character)
		if not Player then return end -- Player officially don't exist
	end
	
	if table.find(WaitingPlayers, Player) then return end -- Player already in this table
	
	table.insert(WaitingPlayers, Player) -- Add Player to WaitingPlayers
	
	UpdateDisplay("Players: " .. #WaitingPlayers .. " / 8") -- Update the Display
end

local function PlayUntouched(hit)
	local Character = hit.Parent
	if not Character then return end

	local Player = Players:GetPlayerFromCharacter(Character)
	if not Player then
		Character = hit.Parent.Parent
		Player = Players:GetPlayerFromCharacter(Character)
		if not Player then return end -- Player officially don't exist
	end

	-- See if player was in the table
	local Index = table.find(WaitingPlayers, Player)
	if not Index then return end
	
	table.remove(WaitingPlayers, Index) -- Remove Player from WaitingPlayers
	
	UpdateDisplay("Players: " .. #WaitingPlayers .. " / 8") -- Update the Display
end

Play.Touched:Connect(PlayTouched)
Play.TouchEnded:Connect(PlayUntouched)
4 Likes

If it’s a circle area, you could use magnitude and a while/heartbeat loop. A magnitude is to check the distance between the player and the center of the circle. If your area is a flat circle and not a sphere, you should make sure when calculating the magnitude that both the y values in both the vectors are the same.

2 Likes

You could also make a if statement to make sure “hit” is the humanoidRootPart so even if the other parts of the character move, it won’t effect it.

I don’t want to have a dozen while/heartbeat loops running in my game though. I’d rather event based stuff over loops

You should be able to do a compromise then, use a touched event to start the detection then use the various area checking methods as @Tommybridge mentions in order to consistently check if the player is still touching/on the part. Get touching parts should be an alternate checking method but I haven’t tested it out.

The issue here isn’t your code, it’s touched being unreliable as always, say for example a player is faster than a frame or constantly jumping, touched will be triggered and gets buggy.

I’d suggest using another method, also heartbeat loops do not lag your game if that’s the issue, or at least if you’re doing checks on the client.

This is all purely server based. I don’t want to rely on client for this, as I feel it’d just add an unnecessary check on the server, when I can just do it all on the server easily.

Well, speaking of “dozens of loops” and “lag”, you’re really putting a heavy info-passing on the server at this point if you do everything on it, it’s better to leave the check on the client to avoid server-lag and have it all rely depending on client’s machine.

But Heartbeat is a function that’s apart of Rlua, won’t cause lag since its itself attached to the game.

Then I’d have to do client-server-client requests, which aren’t immediate in action, and can sometimes take a second or two before stuff happens. I also don’t want to create a remote event for the pure point of this 1 part being touched.

1 Like

I actually made a post on this same problem like this a bit ago. Here:

local function GetTouchingParts(part)
   local connection = part.Touched:Connect(function() end)
   local results = part:GetTouchingParts()
   connection:Disconnect()
   return results
end

local myPart = -- Path in workspace to the part to be checked
local player = -- Path in workspace to the player to be checked for

while true do
    local touchingParts = GetTouchingParts(myPart) -- Returns a table with all touching parts
    
    for i, v in pairs(touchingParts) do
        if v.Parent == player then
            -- Do whatever you'd like
        end
    end
end

From here: OnTouch and TouchEnded help - #5 by MJTFreeTime

EDIT: @Eestlane771 that’s a weird coincendence; you posted that right as I posted mine, and it’s from the same post! :joy:

5 Likes

Rats, I should’ve read the OP more closely! :flushed:

It’s still possible to do this with Touched/TouchEnded in an event-based way:

local part = workspace.Part

-- array of players currently touching this part
-- manipulated with table.find and table.remove because it's guaranteed not to grow larger than 100 players
local playersTouching = {}

function getPlayerFromRootPart(part)
	return part.Name == "HumanoidRootPart" and game.Players:GetPlayerFromCharacter(part.Parent)
end

function onTouch(part)
	local player = getPlayerFromRootPart(part)
	if not player then return end -- do nothing if this isn't a player's rootpart
	if table.find(playersTouching, player) then return end -- player somehow triggered Touched while still touching the part
	table.insert(playersTouching, player) -- player is now in the list of touching players

	-- your code that should run when player enters the region here
	print("Entered", player)
end

function onTouchEnded(part)
	local player = getPlayerFromRootPart(part)
	if not player then return end
	local index = table.find(playersTouching, player)
	if not index then return end -- player somehow triggered TouchEnded without having touched the part
	table.remove(playersTouching, index)

	-- your code that should run when player exits the region here
	print("Exited", player)
end

part.Touched:Connect(onTouch)
part.TouchEnded:Connect(onTouchEnded)

(itself an adaption of something I posted in another thread)

This code only tracks HumanoidRootPart because there’s only one of it in a character. When you want the script to activate on any body part coming in contact with the region, you have to keep track of every part of the character and remove the player when all of its parts have exited. It’s also possible to just keep a count of the parts of the player, but that’s likely to mess up when a part changes parents.

It’s probably possible to Touched, but not TouchEnded a part, and if that ever happens to you, have a script on a wait(2) loop use the above code to check which players are still actually near the part. It’s still looping, but it’s being done a hundred times less often.

4 Likes

You may want to use the Zone+ Module by ForeverHD. It’s the most well thought out and likely foolproof system to detect touch, which uses a combination of ray casting and region3.

Wait, why don’t you just make an invisible cylinder part, make it very tall and set it CanCollide to false and put this code into that.

Maybe use Region3?
Yes you have to check every second if someone is near, but its good because player (or object) does not have to move.
If its in Region you get its instance value.

Try using posistion.Magnitude

local distance = (position1 - position2).magnitude
2 Likes