Player and character should be loaded, but moving HumanoidRootPart doesn't always work

I am trying to automatically set a players team (using my own TeamHandler) when they join, but noticed that setting the HumanoidRootPart doesn’t always work. I thought that I should just wait for both the character to load and its appearence, although this did not fix anything.

The code is written in TypeScript, although it should be fairly self-explanatory.

// Auto-assign players to runners
Players.PlayerAdded.Connect((player) => {
    player.CharacterAdded.Wait();
    player.CharacterAppearanceLoaded.Wait();

    runners.AddPlayer(player);
    runners.SpawnPlayers();
});

^^ This script will automatically assign players to the runners team.

public SpawnPlayers() {

    this.couldNotSpawn = [];

    this.GetPlayers().forEach((player) => {

        // Create new coroutine to avoid thread blocking when finding root

        coroutine.wrap(() => {

            const root = player.Character?.WaitForChild("HumanoidRootPart", 5) as Part | undefined;

            if (root !== undefined) {

                // Choose random location and set the players position to that

                const spawnLocations = this.teamObject.SpawnLocations;

                const index = math.random(0, spawnLocations.size() - 1);

                const position = spawnLocations[index];

                root.Position = position;

            } else {

                print(`${player.Name} could not be spawned`);

                this.couldNotSpawn.push(player);

            }

        })();

    });

}

^^ This script shows the part of the TeamHandler object which spawns players. It can successfuly get all the players and automatically sets their position to a random location.

If someone could explain why the root is not always moved, that would be great.

Yield to override the default spawn system, also CFrames are necessary to update the welds and Motor6Ds of the characters limbs or else you will be nothing but an invisible torso.

1 Like