Add a single-frame wait() directly after the CharacterAdded line.
You need to give the CharacterAdded event a little bit of time to initialize all of the properties that a character can spawn with, and one single frame (which you get with just wait() or hooking some kind of Heartbeat or Stepped event) is more often enough not, enough time for initialization to finish.
You can even see this happening in Roblox’s own example scripts:
local function onCharacterAdded(character)
-- Wait a brief moment before removing accessories to avoid the
-- "Something unexpectedly set ___ parent to NULL" warning
RunService.Stepped:wait()
-- Check for any existing accessories in the player's character
for _, child in pairs(character:GetChildren()) do
destroyAccessories(child)
-- COMMANDHAT: And so on and so forth. This isn't the entire script, just a quote of the relevant part.
No, WaitForChild still returns on the same instant that the HRP exists. You need the frame after the one that WaitForChild returns, in order to teleport.
wait() will work in all cases but has its own drawbacks, namely that you might end up on a later frame then intended. If you have time-sensitive code wait() is not a good idea. If you just want code to run with all instances initialized and don’t particularly care exactly which frame you land on, wait() will work perfectly.
RunService.Stepped is much more time-accurate, but this event will only fire on the server, and will instead silently fail – returning instantly – on the client.
(You’ll need to define the map variable if you want to put this in a different script file then the one you’re working in.)
local iCount = 0
local wtd = true
local itemTable1 = map.Spawns:GetChildren()
local itemTable2 = map.Spawn:GetChildren()
wait(5) -- necessary to allow both getchildren calls to run at the same time; it's 10pm here and I'm getting lazy
iCount = #itemTable1 + #itemTable2
print("NUMBER OF AVAILABLE TELEPORT LOCATIONS:" ..iCount.. "")
local iCount = 0
local itemTable1 = map.Spawns:GetChildren()
local itemTable2 = map.Spawn:GetChildren()
wait(5) -- necessary to allow both getchildren calls to run at the same time; it's 10pm here and I'm getting lazy
if itemTable1 and itemTable2 then
iCount = #itemTable1 + #itemTable2
else if itemTable1 and (itemTable2 = nil) then
iCount = #itemTable1
elseif (itemTable1 = nil) and itemTable2 then
iCount = #itemTable2
else print ("UH OH. THE SCRIPT CAN'T FIND ANY SPAWN LOCATIONS AT ALL.") end
print("NUMBER OF AVAILABLE TELEPORT LOCATIONS:" ..iCount.. "")
edit: Retry the script; just realized there was some stupid CSS issue eating the first line of the script, which defined iCount.
Alright, so now we know the spawns are being located.
I’ve noticed you’re trying to edit the Position of the player; Have you tried editing the CFrame instead?
e.g. hrp.CFrame = CFrame.new(hrp.CFrame.p + CFrame.new(0,4,0))
Why bother with cframes instead?
Historically, Vector3s have always been a bit fidgety and hard to work with, and it’s not uncommon to have a Vector3 math operation simply fail silently because you somehow fed invalid math into the equation without knowing.
CFrames are generally more reliable; in addition, a CFrame defines both position and orientation, which can save you a surprisingly large amount of coding.
It’s easy to transition to cframes; Cframe.p pulls only the position-related values out of the CFrame and drops the rest, which is very handy for position math.