Today I’ll be addressing the reliability issue when using TouchEvents, it’s not as unreliable as you think.
Sure you can use Region3, Raycast, GetTouchingParts or Magnitude instead however those are quite tedious and complex compared to TouchEvents.
and I’ll be showing you the reason why TouchEvents are unreliable is because of your Implementation not Roblox
How it works
Normal Implementation
Touched & TouchEnded fires when touchedPart startsTouching and stopsTouching detectorPart
Special case:
When you Equip/Unequip a Tool Touched & TouchEnded fires
When you change the Character size Touched & TouchEnded fires
These are probably why TouchEvents to seems to be unreliable
The Problem
Detecting the entire Player.Character with no Touch Filters
Not checking if the touchedPart is still inside the detectorPart
Not knowledgeable of how TouchEvent works (due to lack of Documentation)
How to use it Properly
My Implementation
You need to be Detecting Touch on ONLY one Part at a time for Player.Character, which is preferably the HumanoidRootPart
and to prevent those special cases from causing trouble we need to check if the touchedPart is still inside that detectorPart
you can also Connect and Disconnect the TouchEvents for efficiency
Source Code
Code
--| SERVICES:
local plr = game.Players.LocalPlayer
local Cha = plr.Character
local Hum = Cha.Humanoid
local RootPart = Hum.RootPart
--| VARIABLES:
local Red = Color3.new(1,0,0)
local Green = Color3.new(0,1,0)
local USE_NORMAL_IMPLEMENTATION = false
local detectorPart = workspace.Part
--| FUNCTIONS:
local function isPointInPart_Fn(Point, Part)
local relPos = Part.CFrame:PointToObjectSpace(Point)
return math.abs(relPos.X) < Part.Size.X*.5 and math.abs(relPos.Y) < Part.Size.Y*.5 and math.abs(relPos.Z) < Part.Size.Z*.5
end
-- Implementations
--|> Normal Implementation
local function NormalImplementation()
detectorPart.Touched:Connect(function(touchedPart)
if touchedPart == RootPart then
print(".Touched")
RootPart.Color = Green
end
end)
detectorPart.TouchEnded:Connect(function(touchedPart)
if touchedPart == RootPart then
print(".TouchEnded")
RootPart.Color = Red
end
end)
end
--|> My Implementation
local function MyImplementation()
local IsInPart
local touched_Fn, touchEnded_Fn
local touched_Ev, touchEnded_Ev = detectorPart.Touched, detectorPart.TouchEnded
local touched_Connection, touchEnded_Connection
-- touched
function touched_Fn(touchedPart)
if (not IsInPart) and touchedPart == RootPart then
IsInPart = true
touched_Connection:Disconnect()
touchEnded_Connection = touchEnded_Ev:Connect(touchEnded_Fn)
print(".Touched")
RootPart.Color = Green
end
end
-- touchEnded
function touchEnded_Fn(touchedPart)
if IsInPart and touchedPart == RootPart then
if (not isPointInPart_Fn(RootPart.Position,detectorPart)) then
IsInPart = nil
touchEnded_Connection:Disconnect()
touched_Connection = touched_Ev:Connect(touched_Fn)
print(".TouchEnded")
RootPart.Color = Red
end
end
end
touched_Connection = touched_Ev:Connect(touched_Fn)
end
--| SCRIPTS:
if USE_NORMAL_IMPLEMENTATION then
NormalImplementation()
else
MyImplementation()
end
for _,v in ipairs(Cha:GetDescendants()) do
if v:IsA("BasePart") then
v.Transparency = .9
end
end
Cha.DescendantAdded:Connect(function(addedDescendant)
if addedDescendant:IsA("BasePart") then
addedDescendant.Transparency = .9
end
end)
RootPart.Transparency = 0
Cha.Head.face:Destroy()
This is a clever solution which does solve some of the issues originally posed by touched/untouched events:
It’s accurate! No need to worry about untouched firing before untouched now, and modifications of the character within a zone won’t cause any issues as you mentioned.
It’s relatively simple to setup.
There are still limitations which may dissuade its use:
You’re solely limited to the client. This method can’t be implemented on the server, therefore server-side implementations of features such as safe zones are impossible to achieve (without networking, which adds additional complexity).
Other players will still be triggering this event frequently, which is a huge issue as explained in this discussion.
It’s not as easy to setup with complex geometries. You’ll either be forced to convert zones into meshes/unions, or setup additional touched events which become incrementally inefficient.
That’s not true, if the Part only exist on the Client and not on the Server it won’t trigger when other players Touch the Part.
correct me if I am wrong though
I’d recommend using Zone+ for something that requires Precision.
You are right about implementing this ServerSide, it is difficult to do when there is Latency however you could always do Sanity Checks to verify the input.
Okay, imagine this scenario:
You use TouchEvents for swords, and everytime a humanoid limb touches it the target get’s damaged.
Now, what prevents the target from just deleting the touch interest? You can’t detect that, as it’s client sided - the connection would still be there on the server.
Touched events on the client function identically to those on the server, therefore all valid parts, including character parts will still trigger them. That’s why it’s always important to check hit.Parent.Name == localPlayer.Name when using touched events on the client (as you’ve done).
That part is in workspace and it exists on the Server, I said if the part is Client Sided (a Local Part) only then it won’t detect when other players touch it.
This has nothing to do with the TouchEvents being Server or Client sided, I’m talking about the Part being Server or Client sided
I didn’t demonstrate this mechanic in my Resource because it wasn’t necessary, however I’d like to thank you for bringing this up.
TouchEvents definitely has flaws like any other method, henceforth I suggest that we use the appropriate methods to achieve what we want.
That’s correct if you create a part locally (e.g. cloning the server part then destroying it on the client), however the examples provided do not appear to do this. The touched events in the video above were handled on the client in local scripts.
I agree with what you said but using GetTouchingParts introduces time completely, if there are many parts inside the Detector part it becomes inefficient.
Using CFrame to check is more practical since you only need to know about one part not every other part.