Is it good to use repeat wait() loops for this? Is there a more efficient method?

Hi I’m kind of new to making scripts that are relative to waiting before loading data, or stats. This is some code I whipped up that runs a function when the player’s character is in the game.

game.Players.PlayerAdded:Connect(function(player)
	repeat wait() until player.Character or player == nil
	morph(player.userId,character)
	player.CharacterAppearanceLoaded:Connect(function(character)
		morph(player.userId,character)
	end)
end)

All I want to know is if there’s a better way to do this, and what the method is.

3 Likes

There is a much better way to do this. If you’re waiting for the character to load, you can do it in a more efficient variable.

local character - player.Character or player.CharacterAdded:Wait()

This will only yield if the player’s character doesn’t exist yet, and when the character loads, it will stop yielding.

It’s not a good idea to have the player becoming nil as the condition, because if they do become nil, then your script would error, because the player wouldn’t be found.

2 Likes

Player won’t become nil as a variable won’t garbage collect while it has references - You’d need to check player.Parent

Edit: Sorry replied to the wrong person

1 Like

If we are being picky here, it wouldn’t matter if you did or did not have Player == nil seeming as the Character is going to load before the Player is removed anyway as the CharacterAdded event was already fired on the Player joining unless its disabled - also if you checked Player.Parent it would be the game.Players which is basically irrelevant in this scenario.

I think we’re all forgetting the fact that we’re not fully sure how Roblox has adapted Garbage Collection and that scripts etc are pretty instantaneous relative to us and their will be an order within events that the server handles, i.e. the Character will likely be added if calls before the server handles removes the Player.

1 Like

In regards to your efficiency in your method, I think the key thing I’ll point out is the fact your morphing your character twice essentially - once when Player.Character is not nil i.e. the Player model exists and is set to the character and once when the entirety of the characters appearance has loaded - in other words you would be better just cutting out:

repeat wait() until player.Character or player == nil
morph(player.userId,character)

Seeming as this:

player.CharacterAppearanceLoaded:Connect(function(character)
	morph(player.userId,character)
end)

Is going to fire the moment the Player’s Character has fully loaded - this method is good if you want to make certain everything in the Character is deleted etc if your trying to remove accessories for example.

Your methodology is fine however, wouldn’t do much different myself aside from removing the thing above.

1 Like

yes, using repeat wait() until in your code is not often a good idea for stuff this,

in your case you are using it to wait until a condition is met, but even after player.Character exists, your code could still wait unnecessarily.

Use the appropriate event, Player.CharacterAdded as such :

   Player.Character or Player.CharacterAdded:Wait() 
   -- if the player doesn't exist, then it will wait until it is added

Avoiding wait() and why

Edit : replied to the wrong person

2 Likes

BAD. Never, ever use a while wait() or repeat wait() code, as it can easily introduce lag into your game. I would recommend using a wait(numberHere) when possible.

Putting that aside, I have a set of solutions:

  • If you want to have a variable the also keeps the character, and have a method of garbage collection, use local character = player.Character or player.CharacterAdded:Wait(). If the character is nil, it will call yielding function CharacterAdded:Wait(), which returns the character model.
  • If you just want to wait for the character, use: workspace:WaitForChild(player.Name).

I used to use repeat wait() until player.Character in the old days, but it’s just inefficient, lag-inducing, and ugly.

Using local character = player.Character or player.CharacterAdded:Wait() is generally the most professionally used method on Roblox, but the variable does not get “garbage collected”, meaning you have to have some workarounds. If you want to check if your character still exists, I would use:

local doesExist = character and character.Parent and character:FindFirstChild("Humanoid")

Keep in mind that having something like if soandso, the code knows you are implying *if soandso~=nil`. The following code is what the engine sees that works EXACTLY the same, but is not necessarily needed to write:

local doesExist = character ~= nil and character.Parent ~= nil and character:FindFirstChild("Humanoid") ~= nil

I hope I helped :slight_smile:

4 Likes

hi I was wondering if u could help me with my problem as well.

I have a boolvalue call no steal and when u press a button it makes the bool value true, everything seems to work but when it comes to the dunking it breaks. Im not familiar with using repeat so I was wondering if u can tell me what I did wrong or rewrite it.

This is my code:

FixBallEvent.OnServerEvent:Connect(function(player, NoSteal, Ball)
if NoSteal == true then
local character = player.Character
local humanoid = character:WaitForChild(“Humanoid”)
Ball.Equip.Disabled = false
Ball.Ball.Position = character.PrimaryPart.Position
Ball.Ball.Velocity = Vector3.new(0, -1, 0)
wait(1)
if player.NoSteal.Value == true then
Ball.Equip.Disabled = false
Ball.Ball.Position = character.PrimaryPart.Position
wait(1)
repeat wait(1) until player:FindFirstChild(“Ball”)
wait(2)
humanoid.Climbing = true
humanoid.WalkSpeed = 16
Ball.Equip.Disabled = true
end
end
end)

1 Like

It’s a bit difficult to read the code, can you format and indent it please?

FixBallEvent.OnServerEvent:Connect(function(player, NoSteal, Ball)
if NoSteal == true then
local character = player.Character
local humanoid = character:WaitForChild(“Humanoid”)
Ball.Equip.Disabled = false
Ball.Ball.Position = character.PrimaryPart.Position
Ball.Ball.Velocity = Vector3.new(0, -1, 0)
wait(1)
if player.NoSteal.Value == true then
Ball.Equip.Disabled = false
Ball.Ball.Position = character.PrimaryPart.Position
wait(1)
repeat wait(1) until player:FindFirstChild(“Ball”)
wait(2)
humanoid.Climbing = true
humanoid.WalkSpeed = 16
Ball.Equip.Disabled = true
end
end
end)

Can you clarify what’s happening here?

So basically when I press a button the bool value sets itself to true which disables a script from happening. when I dunk the ball I made I fire client that leads to that script to happen but the ball doesn’t return to the player and im trying to make it loop until the ball is found in the player. its hard to explain…

It might just be better to use RunService.Heartbeat instead, and every time that fires check if the ball is touching somebody.

can you write it out for me im still a rookie scripter and im not familiar with heartbeats

I don’t think that will help in the long run, so I’ll point ya to some resources instead:

https://developer.roblox.com/en-us/api-reference/event/RunService/Heartbeat

https://developer.roblox.com/en-us/api-reference/function/WorldRoot/GetPartsInPart

1 Like