Calling the Player:LoadCharacter() with an R15 Avatar fires events in the following order (Note: R6 ordering is different):
Player.Character sets
Player.CharacterAdded fires
Player.Changed fires with a value of “Character”
Character appearance initializes
Player.CharacterAppearanceLoaded fires
Character.Parent sets to the DataModel
The Character rig builds, and the Character scales
Character moves to the spawn location
LoadCharacter returns
Even though you’re updating the CFrame of the HumanoidRootPart after the Character has been added to the Workspace, the Character hasn’t been moved to the SpawnLocation yet. As a result, there needs to be some sort of additional delay before you teleport the Character. Here are some examples:
Example / Comparison Video
Note:
(I had tried this initially with one setup that used CharacterRemoving which wasn’t functioning properly, however, thanks to @MagmaBurnsV’s post in this thread showing that it would work fine, I realized the error I made and swapped it out with Humanoid.Died. Their inclusion of :PivotTo instead of updating the PrimaryPartCFrame was also a good reminder, as I had forgotten about that! )
Example codeblock #1 Using task.defer (recommended over CharacterAppearanceLoaded):
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(Character)
local Humanoid = Character:WaitForChild("Humanoid")
Humanoid.Died:Connect(function()
local currentCFrame = Character.HumanoidRootPart.CFrame
player:LoadCharacter()
task.defer(function()
player.Character:PivotTo(currentCFrame)
warn("Updated position")
end)
end)
end)
end)
At least in an empty baseplate, swapping out Player.CharacterAdded:Wait() with Player.CharacterAppearanceLoaded:Wait() teleports the player’s Character after all of those steps have already happened (even though CharacterAppearanceLoaded still fires even before the Character is moved to a SpawnLocation). However, based on my testing, it happens with a more noticeable delay than the first option (the comparison video can be found above, or by clicking here).
Example codeblock #2 (works, but not recommended)
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(Character)
local Humanoid = Character:WaitForChild("Humanoid")
Humanoid.Died:Connect(function()
local currentCFrame = Character.HumanoidRootPart.CFrame
player:LoadCharacter()
player.CharacterAppearanceLoaded:Wait()
player.Character:PivotTo(currentCFrame)
warn("Updated position")
end)
end)
end)
Apologies for taking a while to edit the post! I wanted to make sure that it would still provide educational value even after my initial suggestion didn’t turn out to be the ideal solution
local currentCFrame = Player.Character.HumanoidRootPart.CFrame
Player:LoadCharacter()
Player.Character:SetPrimaryPartCFrame(currentCFrame)
--Its a better practice using SetPrimaryPartCFrame when it comes to changing the player's position
That’s odd – at least when I tested it in an empty baseplate, swapping it out with Player.CharacterAppearanceLoaded:Wait() appeared to work every time, as shown in the following example:
Edit: Here’s the example / comparison video
But even with that in mind, I would recommend referencing @MagmaBurnsV’s usage of task.defer for this use case, instead. After doing some additional testing in Studio, that seems to be more consistent and teleports the Character with essentially no visual delay in comparison to the method I initially suggested (as shown by the comparison in the video).