Changing player position not working in a metamethod

  1. I want my player to move to a certain room when the game starts. The two tables represent two rooms. When a player gets added to a table, the metamethod for that table has a function that moves a player to that corresponding room

  2. My player is not moving anywhere. No errors btw

  3. I have tried to fix this for a long time and still not able to, I suspect that it is because you can’t move a player inside a metamethod

local roomOne = {}
local roomTwo = {}

local metatable = {__newindex = function (t, i, v) 
v.Character:SetPrimaryPartCFrame(game.Workspace.roomOneTeleportionPort.CFrame)
end}

setmetatable(roomOne, metatable)
setmetatable(roomTwo, metatable)

local function onCharacterAdded(character)
	local player = game.Players:GetPlayerFromCharacter(character)
	roomOne[#roomOne + 1] = player
end

local function onPlayerAdded(player)
	player.CharacterAdded:Connect(onCharacterAdded)
end

game.Players.PlayerAdded:Connect(onPlayerAdded)
2 Likes

There are no weird special limitations like these on metatables. The only thing I can think of is sending tables w/ metatables across server/client boundary, i.e. w/ RemoteEvents. That strips the metatable and just sends the plain table and IIRC is documented somewhere on the wiki. Doesn’t seem to be what you’re doing, just wanted to let you know.


The bug is probably because the default spawning system is weird and you just have to know because Roblox hasn’t “fixed” this yet. It’s kinda documented here, but not in a way that’s useful for beginners or mere mortals trying to figure out why their code isn’t working as expected. It’s been like this for years, and I remember having to deal with a similar bug myself and just being incredibly frustrated because there’s no way to know :confused: \rant

You can’t set the position (or CFrame) of the character just when they spawn. You have to wait for the default spawning system to do its thing, otherwise it sets the CFrame after you, and its version ends up being the one that actually does something.

Sometimes you’ll see people putting in a wait()´, or a renderStepped:Wait()`. I prefer this:

local limb = c:WaitForChild("HumanoidRootPart")
limb:GetPropertyChangedSignal("CFrame"):Wait()

because it better describes exactly what we’re waiting for - the default spawn system getting out of our way. This bug also happens if you turn off Players.CharacterAutoLoads or have no SpawnLocations in your game.

Here’s a test place to prove this happens.

SpawnBug.rbxl (24.2 KB)


Let me know if this fixes your thing or if the issue is something else :sweat_smile:

EDIT: It’s covered by the code examples at Player | Documentation - Roblox Creator Hub

2 Likes

" You can’t set the position (or CFrame) of the character just when they spawn. You have to wait for the default spawning system to do its thing, otherwise it sets the CFrame after you, and its version ends up being the one that actually does something."

Do you mean that when you write lua v.Character:SetPrimaryPartCFrame(game.Workspace.roomOneTeleportationPort.CFrame) it sets the CFrame of character’s primaryPart to the character’s primaryPart itself instead of the teleportionPort? And if so, can you explain why this happens? And in the SpawnBug.rbxl file, there is a chunk: lua if script.HandlesSpawnBug.Value then limb:GetPropertyChangedSignal("CFrame"):Wait() end and I don’t really get what your trying to do here. My guess is that you are trying to check if the boolValue is loaded into workspace and then proceed wait for the limb.CFrame to change. And I don’t get the purpose of it.

1 Like

Here’s a timeline of what happens:

  • The player’s Character is created
  • player.CharacterAdded is fired
  • The listener that you connected (onCharacterAdded) is called
  • onCharacterAdded puts the player in roomOne
  • this calls metatable.__newindex, which calls SetPrimaryPartCFrame, putting the Character in the correct location
  • The built-in spawn system sets the CFrame of the character to whatever (either above (0, 0, 0) or on a SpawnLocation). This is what causes the bug.
  • The built-in spawn system sets the Parent of the Character to Workspace

Because the built-in spawn system sets the CFrame of the Character after firing player.CharacterAdded, your script has time to set the CFrame of the Character before the built-in spawn system. Even though you’re moving the Character correctly, you’re doing it too early because it gets overwritten afterwards, making it look like your code did nothing.

I’m not sure what you mean, but probably no. The only thing that’s going wrong is timing related, as described above.

HandlesSpawnBug is a BoolValue object that can be used to toggle the fix on/off. Checking “if thing.Value” does the same thing as checking “if thing.Value == true”. So if that condition is true, i.e. the BoolValue is checked, then the fix happens.

The actual fix is this part:

limb:GetPropertyChangedSignal("CFrame"):Wait()

limb is just any BasePart inside the Character, in this case HumanoidRootPart because that should exist in all types of rigs (e.g. Torso exists in R6 rigs, not in R15 rigs). The GetPropertyChangedSignal("CFrame") call gets a signal that triggers when the given property is changed. So when the limb moves the signal fires. You can call :Wait() on any signal which causes your program to wait at that point in the code until that signal is fired.

So all in all, it waits for the character to be moved by something else before setting its CFrame, because otherwise it would be overwritten.

Let me know if something is still unclear :slight_smile:

1 Like

Thank you! You answered my question but can I ask another question related to this?