Character Teleporting Issues

tl;dr – player.CharacterAdded:Wait() is acting inconsistent and I need help.

I’m trying to make it so when I start a game, it teleports both players given as arguments p1 and p2 to their respective game board.

function startGame(p1, p2)
	if p1 ~= p2 then
		print("#boards =",#boards)
		for i,v in pairs(boards) do
			if v.InUse == false then -- if we found a board to use, then insert the players in the board's table
				--print(boards[i])
				v.InUse = true
				boards[i].p1 = {["Wins"]=0} --Boards[Board1][Player1] = {[Player1's Stats Here]}
				boards[i].p2 = {["Wins"]=0}
					local character1 = p1.Character or p1.CharacterAdded:Wait()
					character1.HumanoidRootPart.CFrame = workspace.Boards:FindFirstChild(i).BaseBoardParts.Bottom.CFrame * CFrame.new(5,2,0)
					local character2 = p2.Character or p2.CharacterAdded:Wait()
					character2.HumanoidRootPart.CFrame = workspace.Boards:FindFirstChild(i).BaseBoardParts.Bottom.CFrame * CFrame.new(-5,2,0)
				print(p1,"and",p2,"have started a game on",i)
				break
			end
			print(boards[v.Name])
		end
	else print("Error: can't play against self")
	end
end

This is not giving me the results I’d expect. I’m currently reworking my code to be cleaner, so this is a let down that it does not work. I’m referencing the thread Avoid wait() and why to get my characters without repeat wait() in my code.

The result is that it only teleports one player and only some of the time. The odd part is there is also no error reported.

Screenshots:


Log:
image

The important part of the log is that the startGame function is working as intended and says that the two players involved start a game on their respective board.

Objective: Why is it being inconsistent and how can i fix it? I thought :Wait() was supposed to be better than the worse method of doing things. I get why it should be better, but it’s not looking that way right now.

Thanks for your help in advance, I appreciate the time you guys take to respond to my posts!

2 Likes

why are you moving character by changing the humanoid root part CFrame? The two methods of in-game character movement that you should use are the MoveTo function or the SetPrimaryPartCFrame, like this:

--for example this is how you would use SetPrimaryPartCFrame()
character:SetPrimaryPartCFrame(workspace.Boards:FindFirstChild(i).BaseBoardParts.Bottom.CFrame * CFrame.new(5,2,0))
--you can also use the MoveTo
character:MoveTo(--[[A vector 3 position here]])
2 Likes

You can either SetPrimaryPartCFrame like @RomeoEDD mentioned, or you can use MoveTo()
Example here:

local board = workspace.Boards:FindFirstChild(i).BaseBoardsParts.Bottom
local character1 = p1.Character
character1:MoveTo(board.Positon + Vector3.new(5,2,0)) 
local character2 = p2.Character
character2:MoveTo(board.Position + Vector3.new(-5,2,0))
print(p1,"and",p2,"have started a game on",i)
1 Like

It seems a consensus has been reached: I’m using a depricated method to teleport players!

I appreciate your feedback as it helps me with moving on to non-depricated scripting methods!

Do you think by switching to one of the two methods mentioned in your posts it would fix my script’s inconsistent behavior? It was working before with my repeat wait(), the problem was mainly with the :Wait() function I was sure.

there is a bug with using wait() like that, meaning sometimes it may not work you could also try getting the character by doing this

local player = game.Players.LocalPlayer
local character = player.Character or workspace:WaitForChild(Player.Name) or player.CharacterAdded:Wait()
--ofcourse if you have things directly parented to the workspace named after the character this will not work

I would have thought with 3 modes of getting the character that would work, but I’m returning errors…

local character2, mode = p2.Character, "1" or workspace:WaitForChild(p2.Name), "2" or p2.CharacterAdded:Wait(), "3"
					character2.HumanoidRootPart.CFrame = workspace.Boards:FindFirstChild(i).BaseBoardParts.Bottom.CFrame * CFrame.new(-5,2,0)
					print(character2.Name, mode)

Output:
image

This output is variable and generally happens for player 2.

If a new player joins, my queue function runs again and fixes the previous player not being seen, but it’s still an issue that this doesn’t work.

define your character in the following way so that it can yield if the character isn’t there inside of workspace. This should guarantee that the character value won’t ever be nil.

local Character = Player.Character and Player.Character.Parent and Player.Character or Player.CharacterAdded:Wait()

Hope this helped :smile:

Can you break this down for me? I see argument 1, argument 2, argument 1 again, argument 3

Am I missing something @HumbledDragon? Thanks for your reply!

I think the logic here is flawed, if I was to break it down, it doesn’t matter whether p2.Character is nil, it is always returned to character2 with the mode “1”:

p2.Character, “1” or workspace:WaitForChild(p2.Name)

is equivalent to

p2.Character, (“1” or workspace:WaitForChild(p2.Name))

which is the same as

p2.Character, (“1”) -- Since strings are truthy

Hence, your script doesn’t actually yield if there’s no character.
Utilising the method

local character2 = p2.Character or p2.CharacterAdded:Wait()

Is a better way to retrieve a character

Okay so basically, the first portion of this line Player. Character just checks if the property of Player. Character isn’t nil. When using an and statement you are verifying more than one check with the last one being used. So when I did Player. Character and Player.Character.Parent and Player. Character I checked if Player. Character is there if so I go onto check if Player.Character.Parent is present (because when a part is destroyed it is parented under nil so if the character wasn’t in workspace this check would be false and would wait for the character added event to fire). It then goes on to say and Player.Character which as I stated above, now since the other checks came out to be true, this one just sets the Character variable to it (because the last of the and operator is used). If at any point the and operator sees that the statement is false it goes on to the or operator which just waits for the characteradded event to fire to set the character variable. In all honesty, this is just a shorter way to use if statements.

if Player.Character then
   if Player.Character.Parent then
       local Character = Player.Character
   else
       local Character = Player.CharacterAdded:Wait()
   end
else
    local Character = Player.CharacterAdded:Wait()
end
-- Or I guess if you wanted to condense this
if Player.Character and Player.Character.Parent then
    local Character = Player.Character
else
    local Character = Player.CharacterAdded:Wait()
end

Although there is a much more understandable method of doing this that I think could better illustrate to you what exactly is happening

local Character;
while not Player.Character or not Player.Character.Parent do
   wait()
end
Character = Player.Character 

-- Shortened version 

local Character; while not Player.Character or not Player.Character.Parent do wait() end; Character = Player.Character

As you can see aside from the if statement these two do the same thing but the one I provided earlier is much cleaner (the if statements were for visualization purposes on how the and and or operators work)

1 Like

Wouldn’t this error if Player.Character is nil, hence stopping the loop?

@PirateOnThePlank, I appreciate the tip, but that’s why I made this thread. I was following advice exactly akin to what you’re saying and it isn’t working for me, see the OP above for more info. I always appreciate the time you guys take to reply to my posts, I’m just trying to figure this out. It should work, but it’s not.

According to @RomeoEDD, it’s buggy that way, so I’m just trying to figure out what I can do until that is fixed. Or am I implementing the :Wait() function incorrectly?

yh your using it right thats how i do it and it works fine for me

Here’s my test with your code, it’s inconsistent still, not sure what is causing this.

It doesn’t give an error out, the following line prints the character’s name like it should, but it just doesn’t teleport both players. It seems to not find a character, then not yield, and move on like it doesn’t matter. This is confusing for me particularly as I use the error log extensively to debug.

image

function startGame(p1, p2)
	if p1 ~= p2 then
		print("#boards =",#boards)
		for i,v in pairs(boards) do
			if v.InUse == false then -- if we found a board to use, then insert the players in the board's table
				--print(boards[i])
				v.InUse = true
				boards[i].p1 = {["Wins"]=0} --Boards[Board1][Player1] = {[Player1's Stats Here]}
				boards[i].p2 = {["Wins"]=0}
					local Character = p1.Character and p1.Character.Parent and p1.Character or p1.CharacterAdded:Wait()
					Character.HumanoidRootPart.CFrame = workspace.Boards:FindFirstChild(i).BaseBoardParts.Bottom.CFrame * CFrame.new(5,2,0)
					print(Character.Name)
					local Character = p2.Character and p2.Character.Parent and p2.Character or p2.CharacterAdded:Wait()
					Character.HumanoidRootPart.CFrame = workspace.Boards:FindFirstChild(i).BaseBoardParts.Bottom.CFrame * CFrame.new(5,2,0)
					print(Character.Name)
				print(p1,"and",p2,"have started a game on",i)
				break
			end
			print(boards[v.Name])
		end
	else print("Error: can't play against self")
	end
end

For reference, this is the full code with your edits @HumbledDragon

try this i tried rewriting your function:

function startGame(p1, p2)
	if p1 ~= p2 then
		print("#boards =",#boards)
		for i,v in pairs(boards) do
			if v.InUse == false then -- if we found a board to use, then insert the players in the board's table
				--print(boards[i])
				v.InUse = true
				boards[i].p1 = {["Wins"]=0} --Boards[Board1][Player1] = {[Player1's Stats Here]}
				boards[i].p2 = {["Wins"]=0}
                local Character1 = p1.Character and p1.Character.Parent and p1.Character or p1.CharacterAdded:Wait()
				local Character2 = p2.Character and p2.Character.Parent and p2.Character or p2.CharacterAdded:Wait()
					
				if Character1 and Character2 then
					Character1:SetPrimaryPartCFrame( workspace.Boards:FindFirstChild(i).BaseBoardParts.Bottom.CFrame * CFrame.new(5,2,0))
					Character2:SetPrimaryPartCFrame( workspace.Boards:FindFirstChild(i).BaseBoardParts.Bottom.CFrame * CFrame.new(5,2,0))
				    print(p1,"and",p2,"have started a game on",i)
					break
				end
			end
			print(boards[v.Name])
		end
	else print("Error: can't play against self")
	end
end

Guys, I really appreciate the patience. I feel like I’m losing it or something… the logic is and always has been sound, but it’s just not doing what we expect it to do and I can’t explain it. I’ve checked and double checked everything I’m passing to the function, printed out characters and it should work, but it never teleports player 2… This is so weird!

oh wait are you testing it in studio, if you are trying testing it in an actual server there was onetime i was doing something in my code and i didn’t work in solo mode it only worked when i tested it online with a friend of mine

Uh, yes I’ve been doing all of my tests in studio’s “Local Server” with anywhere from 2 to 8 players. Do you think it would be different if I turned on Network Simulator or do you think I really just need real players in a real server?

try it in a real server because i have had things that didnt work when doing it in studio but worked fine online

Can you be my second player? :slight_smile: