Why aren't some parts of my character turning invisible?

I was making a script that would get all the character’s body parts and insert them in a table. However I noticed that not all the parts were being inserted, so I visualized the issue to better understand it. FYI, this is a local script within StarterCharacterScripts.

Script:

local plr = game:GetService("Players").LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()


for i,v in pairs(char:GetChildren()) do
	if v:IsA("Accessory") then v:Destroy() end
	if v:IsA("BasePart") then
		v.Transparency=1
	end
end

This is what is does

I found that this is consistently happening only for the right foot, right hand, and accessories.

If I insert a few second wait, the player is able to turn fully invisible, as seen below:

local plr = game:GetService("Players").LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()

wait(5)

for i,v in pairs(char:GetChildren()) do
	if v:IsA("Accessory") then v:Destroy() end
	if v:IsA("BasePart") then
		v.Transparency=1
	end
end

It seems that the CharacterAdded:Wait() function isn’t working to my expectations. I really don’t want to use the few second wait method either for fear of exploitations and unreliability where ping is concerned.

What can I do to work around this?

Hello, I’m not an scripter but I added a simple line code, to make your face transparent.

local plr = game:GetService("Players").LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()

wait(5)

for i,v in pairs(char:GetChildren()) do
	char.Head.face.Transparency = 1
	if v:IsA("Accessory") then v:Destroy() end
	if v:IsA("BasePart") then
		v.Transparency=1
	end
end

I checked removing the wait time or changing the 5 to 0 and it makes my character to dissaper in the instant.

1 Like

Shouldn’t plr.CharacterAdded:Wait() serve that purpose though?

1 Like

Yeah but wait() is better in some times, try this.

local plr = game:GetService("Players").LocalPlayer
local char = plr.Character
print("omg i'm visible!")

wait(2)

print("what!!! did I dissaper?!")
for i,v in pairs(char:GetChildren()) do
	char.Head.face.Transparency = 1
	if v:IsA("Accessory") then v:Destroy() end
	if v:IsA("BasePart") then
		v.Transparency=1
	end
end
1 Like

yea works well even with wait(). cheers

1 Like

If you don’t want to wait you can just make wait(0) or your choise.

1 Like

any idea why CharacterAdded:Wait() doesn’t work though?

1 Like

The CharacterAdded event fires when the player’s Character spawns but the Character’s clothing items might not have loaded in by that point, which is something that its documentation page highlights:

The CharacterAdded event fires when a player’s character spawns (or respawns). This event fires soon after setting Player.Character to a non-nil value or calling Player:LoadCharacter(), which is before the character is parented to the Workspace.

Note that the Humanoid and its default body parts (head, torso, and limbs) will exist when this event fires, but clothing items like Hats, Shirts, and Pants may take a few seconds to be added to the character.


Instead of adding an arbitrary wait(#) at the top of the script, a more reliable solution (that would also improve the user experience since the player would not need to wait 5 seconds before turning invisible) would involve using Player.CharacterAppearanceLoaded, as that event only fires after the entire appearance of the Character model has loaded:

This event fires when the full appearance of a Player.Character has been inserted.

A Player.Character generally has a range of objects modifying its appearance, including Accoutrements, Shirts, Pants and CharacterMeshes. This event will fire when all such objects have been inserted into the Player.Character.


Example Revision

-- Code for a LocalScript that is placed into the StarterPlayerScripts
local Players = game:GetService("Players")
local player = Players.LocalPlayer

player.CharacterAppearanceLoaded:Connect(function(Character)
    for _, v in Character:GetDescendants() do

        if v:IsA("Accessory") then
            v:Destroy()

        elseif v:IsA("BasePart") or v:IsA("Decal") then
            v.Transparency = 1
        end

    end
end)

Edit: If you want the Character to be invisible from the perspective of other players, too, the code would need to be run from the server side. Here’s an example of that:

Example Revision #2

-- Code for a Server Script placed into the ServerScriptService
local Players = game:GetService("Players")

local function onPlayerJoin(player)
	
	player.CharacterAppearanceLoaded:Connect(function(Character)
		for _, v in Character:GetDescendants() do
			
			if v:IsA("Accessory") then
				v:Destroy()
				
			elseif v:IsA("BasePart") or v:IsA("Decal") then
				v.Transparency = 1
			end
			
		end
	end)
	
end

for _, player in Players:GetPlayers() do
	task.spawn(onPlayerJoin, player)
end
Players.PlayerAdded:Connect(onPlayerJoin)

first code’s not working, my player is still entirely visible

1 Like

there aren’t any errors either

2 Likes

i applied this script to another experience and placed the script in starterplayerscripts, but still entirely visible

2 Likes

Is the code included in a LocalScript in the StarterPlayerScripts container? If it’s currently in the StarterCharacterScripts, consider moving it to StarterPlayerScripts, as that would ensure that it doesn’t need to create a new LocalScript every time the player’s Character respawns, since the event would fire every time the player’s Character appearance loads.

1 Like

glitch with roblox studio you reckon?

1 Like

For now, try testing it in a brand new starter place; I’ve tried restarting playtests as well as repeatedly respawning my Character in a brand new place and it has worked every time:



By chance, do you have a custom StarterCharacter for either of those games?

1 Like

nope


2 Likes

Huh, that’s super strange; I don’t know at the moment why it wouldn’t be working.

Does the same thing happen with the server-sided script example (when the code from Example Revision #2 is placed into a server Script in the ServerScriptService)?

server script service one works fine and makes the whole body invisible. could any settings on roblox studio be making the local script not work?

1 Like

It’s good that the server script one is working at least; as far as the LocalScript goes, if the LocalScript code isn’t working properly with any avatar you equip in both of these situations:

  • upon joining the game / starting the playtest
  • in addition to respawning the Character model more than one time

then I have no idea what would be causing that to happen, as I have not encountered that issue at all since first testing that code around an hour ago throughout several playtests, where my Character respawned over a dozen times in total.


However, after searching around on the Developer Forum, I found another thread where someone was experiencing the same issue.

It seems like there’s a decent chance that it’s not working in a LocalScript as a result of a bug that has been around since at least 2019, but I have no idea what would be causing it to only occur for some users. The recommended workaround from that thread was to listen for CharacterAppearanceLoaded to fire from a server script (which appears to be more reliable since it worked for you and the original poster who experienced the same issue from the thread referenced below):

how would the reliability of using an arbitrary wait value compare to this method? like are there any drawbacks to using it? as far as I’m concerned it works alright and is way simpler anyway

1 Like

I won’t be able to explain the technical details behind it since I’m not as knowledgeable about the specific inner-workings, but I can offer a general overview:


  • Arbitrary wait time

    • No guarantee that it will wait for a long enough amount of time.

    • Not flexible to different situations. It might wait the perfect amount of time for one user, but not another.

    • The longer the wait, the more likely it is to guarantee it has waited for enough time. However, this comes at the expense of the user experience. As players wait longer before having visual feedback that something has worked / reached its intended state, the game may feel less polished from the perspective of the player.

And going back to something you mentioned in the original post:

Although I don’t know many details about the intended gameplay for your game, if that few seconds of the Character being visible upon respawning has the potential of providing a gameplay advantage for someone in the game, then waiting for an arbitrary amount of time will always leave the door open for the average player to take advantage of that.


  • Events (in this case, CharacterAppearanceLoaded)
    • On the server-side, guaranteed to fire once the Character’s appearance has loaded.

    • Flexible to different situations / loading times. Whether a Character’s appearance loads almost instantly, or if it takes a few seconds, the event will fire at the right time.

    • Because the relevant code for updating the Character’s visibility would run as soon as the event fires, this would provide nearly instantaneous visual feedback for the player while simultaneously lessening the likelihood that players would be able to take advantage of the timeframe where the Character model would be visible.


Aside from the odd, inconsistent bug that prevents it from working specifically on the client-side for some users (which is a glitch which doesn’t happen with practically all other client-side-compatible events, and is something that should have been resolved by now) I can’t think of any common drawbacks for using the event over a wait.

The only situation I can think of where there could be an issue would be if Roblox’s Avatar services go offline while games are still playable (which very rarely happens) causing Character appearances to never load. However, for something like that, it would be best to have additional code to fall back to rather than having the entirety of the code revolve around a wait.

  • An example implementation of that could be to store a true or false value for each player that would indicate if their Character model is invisible. It would be set to true after the CharacterAppearanceLoaded event fires.

    Before that event fires, though, a separate CharacterAdded event would fire, setting the value to false and then starting a timer with the RunService. If the value is set to true, the timer can be stopped because the event fired. However, if it’s not set to true after a given amount of time, that would indicate that CharacterAppearanceLoaded never fired. As a result, it can be instructed to run the same code that would loop through the Character model and make it invisible.

    If you want it to be even more reliable, you could connect the DescendantAdded event to the Character model so that it will be able to detect the addition of any new Accessories, BaseParts, Decals, etc. in order to destroy it or update its Transparency right away (even if the CharacterAppearanceLoaded event had already fired).

    All of this helps ensure that the Character model would become invisible, even in unexpected situations.


Since it appears to be reliable when used on the server-side, will immediately fire the event and run the code as soon as it’s ready, alongside the benefit of automatically replicating the visibility changes to every player in the game (because it’s being run on the server side), utilizing CharacterAppearanceLoaded for this use case provides much more reliable and timely visual feedback for the players in the game, in comparison to adding a wait before looping through the Character model.


Although adding a wait works in theory and it could work in the vast majority of situations, it is generally advised to make use of events where possible, as events are much more versatile and are designed in a way to make it faster and easier for the code to respond / react to what’s happening in the game.

Ultimately, for a gameplay feature as simple as this, it’s unlikely for there to be many long-term consequences that could negatively affect the game as a whole by using wait over an event. However, it does have an impact on future gameplay design decisions you might make and how one’s coding practices and relevant skills develop.

If relying on wait becomes a common solution in your scripting toolbox, it could inadvertently impact other parts of the game that are created in the future. What could start with a few seconds of waiting after the Character respawns could turn into cumulative minutes of unnecessary waiting for a variety of things to happen in the game if other features are also designed around waits where events could be used.

On a similar note, waits are often considered to be a “code smell” by other developers; that thread provides some insight into some alternatives of wait with adjacent examples.


There are still valid use cases for waits and waiting through the RunService, especially in circumstances where there’s code that needs to be run indefinitely, but for situations like these where there’s an event that is really suitable for the use case, events usually end up making it possible to create a more robust implementation of specific gameplay features.


I can’t force you to use one option over the other, but I hope I was able to provide some valuable insight about this subject matter.


TL;DR / Key Takeaways

  • Use event-based programming when possible.

  • waits are not bad on its own; it depends on how and when it’s being used.

    • *(side note, wait() has been deprecated in favor of the newer task.wait(). It’s recommended to replace existing wait() calls with task.wait(), if not swapping to other methods of yielding).
1 Like