I think it might be better to create a large part above the pad to act like a zone.
You can then check which player is within the zone and teleport using that data.
This would solve the wonkiness with .Touched checks with characters’ feet.
Here is a working version of the script, it uses a raycast to check if the player is in the zone, as well as a touched event to see when they first enter (read comments in the code and ask if you need more of an explanation):
local TeleportService = game:GetService("TeleportService")
local teleportID = 14627040487
local circle = script.Parent
local timer = 10
local PlayerTable = {}
circle.Touched:Connect(function(hit)
--// try to get player
local plr = game.Players:GetPlayerFromCharacter(hit.Parent) or game.Players:GetPlayerFromCharacter(hit.Parent.Parent)
if plr and not table.find(PlayerTable, plr) then
--// This if statement checks if the player was found and if they're not already in the table
table.insert(PlayerTable, plr) -- adds plr to table
end
end)
while wait(1) do
timer -=1
print(timer)
--// Check to make sure player is still in the part
for _, plr in PlayerTable do
local char = plr.Character
if not char then continue end
--// Ray Origin is where the raycast starts
local RayOrigin = char.PrimaryPart.Position
local RayDirection = Vector3.new(0, -5, 0) -- -5 on the Y will make the ray go -5 studs down from the origin
local Params = RaycastParams.new() -- You can make parameters
Params.FilterType = Enum.RaycastFilterType.Include
Params.FilterDescendantsInstances = {workspace.TpParts} -- will only hit parts in this folder
local ray = workspace:Raycast(RayOrigin, RayDirection, Params) -- make the raycast
if ray then
local hit = ray.Instance
if hit then
print(hit)
--// The if below will check if the player is still in the circle
--// if they aren't then they will be removed from the table
if hit == circle then continue
else table.remove(PlayerTable, plr) end
end
end
end
if timer == 0 then
timer = 10
TeleportService:TeleportAsync(teleportID, PlayerTable) --Teleport players
PlayerTable = {} -- Reset plr table
end
end
Important
Put the circle part inside a folder in the workspace called TpParts
The problem with this, imo, is that there isn’t a real good way to check when they leave the zone (other than doing something similar to what I did in my script, but then there’s no need for an extra part) because TouchEnded will fire on and off when the player moves inside the part (in my past experiences)
Here’s the thing, you shouldn’t be using .Touched in the first place to check if a part is within another part. I’m pretty sure there is a method in workspace called workspace:GetPartsInPart() which would be better. You can then check if that part belongs to a player and add them to the table, instead of ray casting.
Yes there is a method like this, I have used both this and the GetTouchingParts method in the past both ways were very inconsistent and would basically just detect whenever, which last I checked seem to be a common error. .Touched usually does work (sometimes multiple times but that is why we have a check so we don’t add the player twice) and then to confirm instead of using something like TouchEnded (which would most likely cause mayhem) we use a raycast which should be by far them most accurate
Interesting.
From what I’ve observed, .Touched and .TouchedEnded are events, and only fire when something starts touching or stopped touching a part respectively, not when it is actively touching the part.
:GetTouchingParts() and :GetPartsInPart() returns a table of parts that are essentially inside the other part, and work best when used as a zone.
The red part is the zone to use the getParts method on, not the blue platform.
Raycasting, while it does work for finding parts, is a tad-bit too complicated for something as simple as getting players in a region, requiring that you get a list of players from the .Touched or other functions and then doing the raycast.
I believe the best way to do this would be something like this:
local PlayersService = game:GetService("Players")
local TeleportService = game:GetService("TeleportService")
local teleportID = 14627040487
local zone = script.Parent --// the part encasing the floor pad
local timer = 10
local PlayerTable = {}
while task.wait(1) do
timer -=1
print(timer)
if timer == 0 then
timer = 0
local parts = workspace:GetPartsInPart(zone) --// get the parts inside of the zone
for _, part in parts do
local model = part:FindFirstAncestorOfClass("Model") --// check if the character model exists
if model and model:FindFirstChildOfClass("Humanoid") then --// check if the model has a humanoid
local player = PlayersService:GetPlayerFromCharacter(model) --// check if the character is a player
if player and table.find(PlayerTable, player) == nil then
table.insert(PlayerTable,player) --// add them to the table
end
end
end
TeleportService:TeleportAsync(teleportID, PlayerTable) --// teleport them
task.wait(1)
timer = 10 -- restart the cycle
end
end
I think this is a bit more readable and works all the time from my testing.
Using the image example from above as the test case here.
Ray casting is a method where Roblox iteratively steps along a line until it hits something.
If you’ve ever taken an algebra class, then you should know a ray as a point that goes on forever.
Roblox breaks the ray into segments and checks whether the segment hits something.
oooh okay yeah that makes sense.
Thanks so much for your help, also super quick simple question: there is a surface GUI on a sign in the circle so that you can see how long until teleport, but its not updating when I put text = timer… something about the client server relationship I think
local PlayersService = game:GetService("Players")
local TeleportService = game:GetService("TeleportService")
local teleportID = 14627040487
local text = workspace.Text.SurfaceGui.Frame.TextLabel.Text
local zone = script.Parent --// the part encasing the floor pad
local timer = 10
local PlayerTable = {}
while task.wait(1) do
timer -=1
print(timer)
text = timer
if timer == 0 then
timer = 0
local parts = workspace:GetPartsInPart(zone) --// get the parts inside of the zone
for _, part in parts do
local model = part:FindFirstAncestorOfClass("Model") --// check if the character model exists
if model and model:FindFirstChildOfClass("Humanoid") then --// check if the model has a humanoid
local player = PlayersService:GetPlayerFromCharacter(model) --// check if the character is a player
if player and table.find(PlayerTable, player) == nil then
table.insert(PlayerTable,player) --// add them to the table
end
end
end
TeleportService:TeleportAsync(teleportID, PlayerTable) --// teleport them
task.wait(1)
timer = 10 -- restart the cycle
end
end