Player spawning function is VERY unreliable

Hey devforum! I have a module function that is supposed to handle player spawning, but sometimes setupPlayer isn’t called, other times the character isn’t repositioned, etc. Help is greatly appreciated, since my game doesn’t work without this function :sob:

function module.handlePlayerLoading(spawnL, extras, team, otherFunc)
	
	spawnL.Enabled = true

	local function onCharacter(plr, char)
		
		local hrp = char:FindFirstChild("HumanoidRootPart")
		if not hrp then
			hrp = char:WaitForChild("HumanoidRootPart", 3)
		end

		if hrp and spawnL then
			task.wait(0.3)
			hrp.CFrame = spawnL.CFrame * CFrame.new(0, 3, 0)
			print("Repositioned", plr.DisplayName)
		else
			warn("Failed to reposition " .. plr.Name)
		end
		
	end

	local function setupPlayer(plr)
		plr.Neutral = false
		hideLoadingEV:FireClient(plr)

		if team then
			plr.Team = team
		end

		if otherFunc then
			pcall(function()
				otherFunc(plr)
			end)
		end

		plr.CharacterAdded:Connect(function(char)
			onCharacter(plr, char)
		end)

		if plr.Character then
			onCharacter(plr, plr.Character)
		else
			plr:LoadCharacter()
		end
	end
	
	for _, plr in pairs(Players:GetPlayers()) do
		setupPlayer(plr)
	end
	Players.PlayerAdded:Connect(setupPlayer)
	
end

The code seems rather complicated in certain parts. It may also be better to utilize MoveTo rather than change the RootPart’s Coordinate Frame to spawn the player. Something else I’m sort of confused about is why the code rather than a SpawnLocation?

Anywho, let’s go through this.

When you supply a timeout for WaitForChild it will check if the object exists. You can simply do

local hrp = char:WaitForChild("HumanoidRootPart", 3)

Since spawnL is required in the beginning instructions (before onCharacter setup) it’s redundant to check if it’s truthy.

You could simply set it to team or the player’s current team.

plr.Team = team or plr.Team

You could have pcall simply call otherFunc without needing an anonymous function to wrap it.

pcall(otherFunc, plr)

It may be better to connect PlayerAdded before running through the players.


To fix the problem, there’s a couple of things you could try:
A) Use CharacterAppearanceLoaded, or
B) Use MoveTo to move the player’s character to that location. There’s also apparently something called PivotTo which may be handy.

2 Likes

thanks, ill try these

(P.S I heard about MoveTo but apparentally it’s just to make the humanoid walk to the location, not actually set the position or CFrame)

1 Like

Yeah, there’s a method of the exact same name intended for the Humanoid, but there’s one for the Model class. :woozy_face: One’s like alright walk over there ^ - ^ while the other’s like you move now **uses godly powers to move your very being**

1 Like

MoveTo only changes position, only works if the model is unanchored and has a primary part, can fail silently.

PivotTo sets both position and rotation, works with both anchored and unanchored and is consistent and reliable.

Just a slight suggestion, but it may also be worth adjusting the upwards CFrame to account for the player’s height. Just so that it doesn’t look kind funny seeing a larger avatar partially in the ground for a split second lol. This can be accomplished with GetModelCFrame.

local hrp = char:WaitForChild("HumanoidRootPart", 3)
local heightAdjustment = char:GetModelCFrame().Y / 2
heightAdjustment = CFrame.new(0, heightAdjustment, 0)

if hrp then
	hrp.CFrame = spawnL.CFrame * heightAdjustment
else
	char:PivotTo(char:GetPivot() * heightAdjustment)
end

Or for those who prefer non-deprecated methods,

local hrp = char:WaitForChild("HumanoidRootPart", 3)
local _, heightAdjustment = char:GetBoundingBox()
heightAdjustment = CFrame.new(0, heightAdjustment.Y / 2, 0)

if hrp then
	hrp.CFrame = spawnL.CFrame * heightAdjustment
else
	char:PivotTo(char:GetPivot() * heightAdjustment)
end
1 Like