How to find HumanoidRootPart after death?

A few things I want to clear up in this conversation:

  1. Don’t assume HumanoidRootPart will always exist.
    Technically, CharacterAdded will fire and guarantees all the Humanoid and it’s limbs will exist, but when referencing a character, there is a possibility the HumanoidRootPart will be gone (like falling off the map).

  2. If the HumanoidRootPart is constantly not being found, answer these questions:

  • Is your character reference always up-to-date? - Player.Character is a property with a value of the current existing character. If the player respawns, the old character is no longer referenced.
  • Is Players.CharacterAutoLoads set to true and you are spawned in when the code runs? - If this is set to false, the player will not respawn, therefore not updating the Character property.
  • Is your HumanoidRootPart being renamed somehow? - Check the explorer window to see if HumanoidRootPart actually exists.
  • Are you using a custom character model? - You may not have a part called HumanoidRootPart.
  1. You can technically have a top-level characters variable, but you would have to constantly update that table whenever the a player respawns. If you want to guarantee characters with HumanoidRootParts, consider this pattern instead:
local function getCharacters(withHumanoidRootPart: boolean?): {Model}
    local characters: {Model} = {}

    for _: number, player: Player in Players:GetPlayers() do
        if player.Character ~= nil then
            if withHumanoidRootPart == true and player.Character:FindFirstChild("HumanoidRootPart") == nil then
                continue
            end

            table.insert(characters, player.Character)
        end
    end

    return characters
end

-- teleport all characters with HumanoidRootParts
for _: number, character: Model in getCharacters(true) do
    character.HumanoidRootPart.CFrame = CFrame.new(100, 100, 100)
end
1 Like

It will work on server-side too, the server-script will be parented to each character. So the parent of that server-script will be the parent of that character. It’s basically local (sort of)

1 Like

How would you get the scripts to run parented on all chars since StarterPlayerScripts only works with local ones?

That’s not true, don’t know who told you that. Both local and server scripts work on starterplayerscripts.

I tried a print statement in a script in starterplayerscripts and it did not print
image

Within your if statement here is what you should do in different scenarios:

While loop:

while rootpart do
task.wait(5)
print(rootpart.Name)
end

It will only run the code while the root part exists.

If statement with delay:

local rootpart = character:WaitForChild(“HumanoidRootPart”, 10)
if not rootpart then return end
task.wait(0.5)
if not rootpart then return end
--do as you wish with the root part

All you need to keep in mind is that whenever you wanna do something with the root part it may or may not exist. On the line before you manipulate it, check if it exists. If not, exit the function if the rest of the function is related only to the root part.
Exit a function using return, or a while or for loop using break. If the for loop is iterating instances of which you must check more, use continue.

What if the function contains other important things that are unrelated to the root part? Then simply wrap all code relating to the root part in an if statement which checks for the root part’s existence, or start an entirely new local function dealing with the root part with our previously mentioned error-prevention methods within.

1 Like

Thank you, thank seems to work! Since the loop will stop for 10 seconds if they cannot find the player, I am worried this could be exploited. Do you know if running each char in the pairs(characters) loop in parallel would prevent this issue, and if so, how?

I’m not sure I know exactly what you’re talking about, mostly because I’m not sure in what way the script will be trying to access the root part. You can adjust the 10 to a shorter time such as 1, or perhaps 2, if your worried about the code yielding for too long.

If even 1 second is too long, then yes, running the loops in parallel would be much better since one yield wouldn’t stop the entire for loop. I need to understand better in what way you’re accessing the root part, can you post that section of the code?

Sure, by exploiting I mean deleting the humanoid over and over again locally to stop the script from getting past the wait for child line.
Here it is:

local success, hrootPos = pcall(function()
			return char:WaitForChild("HumanoidRootPart", 10).Position
		end)
		
		if success then
                   -- do script
        end

You can do two things:

Simpler:

local players = game:GetService("Players")

players.PlayerAdded:Connect(function(player)
	player.CharacterRemoving:Connect(function()
		local rootpart = player.Character:FindFirstChild("HumanoidRootPart")
		if not rootpart then print("too late to check position") return end
		local position = rootpart.CFrame
		-- or rootpart.Position whichever you prefer
	end)
end)

This completely new approach below means that your code will log the rootpart’s position every 2 seconds. Even if the rootpart is destroyed, its last position is stored in the player(not character since the character is constantly changing)

local rootpartcheck = coroutine.create(function()
	while true do
		task.wait(2)
		for i, player in players:GetPlayers() do
			local rootpos = player:FindFirstChild("rootpartpos")
			if not rootpos then continue end
			local character = player.Character
			if not character then continue end
			local rootpart = character:FindFirstChild("HumanoidRootPart")
			if not rootpart then continue end
			rootpos.Value = rootpart.CFrame
		end
	end
end) 

players.PlayerAdded:Connect(function(player)
	local rootpartpos = Instance.new("CFrameValue")
	rootpartpos.Name = "rootpartpos"
	rootpartpos.Parent = player
end)
1 Like

Thanks so much, so would I do this:

while task.wait(2) do
    for i, player in players:GetPlayers():
        rootpartcheck(player)
    end
end
game.Players.PlayerAdded:Connect(function(player)
    player:SetAttribute("LastPosition", CFrame.new(0, 1, 0))
    player.CharacterAdded:Connect(function(character)
        local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
        character:PivotTo(player:GetAttribute("LastPosition"))
        task.spawn(function()
            repeat task.wait(2)
                player:SetAttribute("LastPosition", humanoidRootPart:GetPivot())
            until character:FindFirstChildWhichIsA("Humanoid").Health <= 0
        end)
    end)
end)

If I’ve understood correctly your initial game design, you want to save the player’s last position every 2 seconds and when the player respawns, they teleport there.

1 Like

I’m creating a chunk generation algorithm and therefore need the player’s position, but the problem is the script stops. My script loops through all characters whenever it cannot find the specified character’s humanoidrootpart because their character is destroyed the script halts since it uses waitforchild and therefore can be exploited (for example, the exploiter could be destroying and undestroying the humanoidrootpart so the script would never carry on)

I need a loop that every 2 seconds checks every player’s position and does not effect other people’s loop/cause errors/warns if that humanoidrootpart doesn’t exist that is server-side.

Do you know of any solutions?

Ah, simple enough I suppose.
Try this:

local function checkForHRPs()
    for _, player in game.Players:GetPlayers() do
        task.spawn(function()
            if not player.Character then warn(string.format("%s does not have a character! Skipping loop for %s...", player.Name)) continue end
            if not player.Character:FindFirstChild("HumanoidRootPart") then warn(string.format("%s does not have a HumanoidRootPart though has a character!", player.Name)) continue end
            local HRP = player.Character.HumanoidRootPart
            print(string.format("%s is healthy!\nX: %d\nY: %d\nZ: %d", player.Name, HRP.CFrame.X, HRP.CFrame.Y, HRP.CFrame.Z))
        end)
    end
end

while task.wait(2) do
    checkForHRPs()
end

No you would put(underneath all the code)

coroutine.resume(rootpartcheck), sorry, I was conducting a coding lesson.

1 Like

Ah, thank you!
For some reason the chunkFlush doesn’t seem to be working, can you spot an issue?:

while task.wait(chunkUpdateTime) do
	for charNum, char in pairs(characters) do
		coroutine.resume(chunkFlush, char)
	end
end
local chunkFlush = coroutine.create(function(char)
		
	local success, hrootPos = pcall(function()
		return char:WaitForChild("HumanoidRootPart", 2).Position
	end)
		
	if success then
		-- do thing
    end
end)
local players = game:GetService("Players")

local rootpartcheck = coroutine.create(function()
	while true do
		task.wait(2)
		for i, player in players:GetPlayers() do
			local rootpos = player:FindFirstChild("rootpartpos")
			if not rootpos then continue end
			local character = player.Character
			if not character then continue end
			local rootpart = character:FindFirstChild("HumanoidRootPart")
			if not rootpart then continue end
			rootpos.Value = rootpart.CFrame
		end
	end
end)
coroutine.resume(rootpartcheck)

players.PlayerAdded:Connect(function(player)
	local rootpartpos = Instance.new("CFrameValue")
	rootpartpos.Name = "rootpartpos"
	rootpartpos.Parent = player
end)


local function chunkFlush(player)
	local rootpos = player:FindFirstChild("rootpartpos")
	if rootpos then
		local position = rootpos.Value
		-- do as you wish with it's position
	else
		print("fail")
	end
end

while task.wait(chunkUpdateTime) do
	for charNum, char in pairs(characters) do
		local player = players:GetPlayerFromCharacter(char)
		chunkFlush(player)
	end
end
1 Like

Also keep in mind I’ve chosen to use CFrame, really the variable should be rootpartcframe not rootpartpos(a misnomer on my part). If you need it’s position you would just do rootpartcframe.Position

1 Like

No not starterplayerscripts, startercharacterscripts.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.