Character Creation Hair Choices

I am making a charactercreations screen, like so;

Basically when you click ‘male’ the hair options for male appears on the hair choices later, one by one, via a table, for female, same thing.

I am having trouble with the actual hair choices option. Here is the table that I am using;

local HairChoices = {
	
	MaleHairs = {
		
		["Bald"] = nil;
		["Trecky Hair"] = 32278814;
		["Afro"] = 5100703261;
		["Blowout"] = 5269022822;
		["Edgar Cut"] = 12654750434;
		["Medium Fade"] = 4584980640;
		["Middle Part"] = 7097720734
		
	};
	FemaleHairs = {
		["Bald"] = nil;
		["Popstar Hair"] = 5890690147;
		["Black High Ponytails"] = 13413181958;
		["Wavy Side Part w/ edges"] = 6346367086;
		["Pigtails"] = 5769262031;
		["Wavy Ponytail"] = 6238509303;
		["Messy Buns"] = 10690181802
		
	}
	
}

Here is the function that updates the hair choices, which gives the dummy their hair using InsertService

local function updateHairSelection()
	
	if not ClickedMale and not ClickedFemale then return end
	print("Player has clicked one or the other")
	-- Update the hair selection based on the currentIndex
	local currentHair = nil
	
	if ClickedMale then
		print("player has clicked male, showing male hairs")
		
		currentHair = HairChoices.MaleHairs[currentIndex.MaleHairs]
		return currentHair
		
	elseif ClickedFemale then
		print("player has clicked female, showing female hairs")
		currentHair = HairChoices.FemaleHairs[currentIndex.FemaleHairs]
		return currentHair
	end
	
	-- Do something with the currentHair value, like updating an image or text label in the frame

	-- Insert the hair accessory on a dummy
	local dummy = game:GetService("InsertService"):LoadAsset(currentHair)
	
	local hair = dummy:FindFirstChildWhichIsA("Model"):FindFirstChildWhichIsA("Accessory")
	
	
	
	if hair then
		
		hair.Parent = CharDummy -- Change "script.Parent" to the location where you want to place the hair accessory
		
	end
	
	
	
end

Whenever you click on the arrow, it’ll go to the next hair, destroying the previous one, and adding the new one, however here is the problem:

  1. It is NOT even adding the accessory, despite having the correct asset id, which I have tested out beforehand using the commandbar

  2. The textlabel that is meant to show the choice names for the hair doesn’t even give the name, like it’s supposed to give, “Trecky Hair”, it just puts in a number

Here is the rest of the code

RightArrow.MouseButton1Click:Connect(function()
	if ClickedMale then
		
		currentIndex.MaleHairs = currentIndex.MaleHairs + 1
		HairChoiceLabel.Text = currentIndex.MaleHairs
		updateHairSelection()
		
		if currentIndex.MaleHairs > #HairChoices.MaleHairs then
			
			currentIndex.MaleHairs = 1
			
		end
		
		
	elseif ClickedFemale then
		
		currentIndex.FemaleHairs = currentIndex.FemaleHairs + 1
		HairChoiceLabel.Text = currentIndex.FemaleHairs
		updateHairSelection()
		
		if currentIndex.FemaleHairs > #HairChoices.FemaleHairs then
			
			currentIndex.FemaleHairs = 1
			
		end
		
	end
	
	
	
end)

LeftArrow.MouseButton1Click:Connect(function()
	if ClickedMale then
		
		currentIndex.MaleHairs = currentIndex.MaleHairs - 1
		HairChoiceLabel.Text = currentIndex.MaleHairs
		updateHairSelection()
		
		if currentIndex.MaleHairs < 1 then
			
			currentIndex.MaleHairs = #HairChoices.MaleHairs
			
			
		end
		
	elseif ClickedFemale then
		
		currentIndex.FemaleHairs = currentIndex.FemaleHairs - 1
		HairChoiceLabel.Text = currentIndex.FemaleHairs
		updateHairSelection()
		
		if currentIndex.FemaleHairs < 1 then
			
			currentIndex.FemaleHairs = #HairChoices.FemaleHairs
			
		end
		
	
	
	end
end)

Here is a video on how it is working currently

Any help would be appreciated

9 Likes

Hey!

I noticed that you’re trying to access the hair using a number as an index, even though each element in the table has a string index?

Like…

local pets = {
   ['cat'] = 'poopoo'
}

print(pets[1]) -- what you're trying to do --> nil
print(pets['cat']) -- what you're supposed to be doing
4 Likes

So how would I do this through my script? Are you talking about this line

currentHair = HairChoices.MaleHairs[currentIndex.MaleHairs]
3 Likes

Yes, that’s what I am talking about.

Probably a simple fix would be editing the table to something like:

MaleHairs = {
	{'Trecky Hair', 32278814},
	{'Afro', 5100703261},
}

And then swapping this line:

currentHair = HairChoices.MaleHairs[currentIndex.MaleHairs]

with

currentHair = HairChoices.MaleHairs[currentIndex.MaleHairs][2]
-- to get second element of the table which is the ID of our hair model.

And for the text we will use:

HairChoiceLabel.Text = HairChoices.MaleHairs[currentIndex.MaleHairs][1]
--to get the first element of the table which is the name.
3 Likes

So the text works now, however;

  1. The insertservice function part is not working, like at all, it’s not parenting the accessory the characterdummy, however there are no errors.

  2. Even though the text works, if I click the arrow after I have reached the max indexed name, It will give me this error,

Players.diordy.PlayerGui.CharacterCreationsScript:612: attempt to index nil with number  

image

And it goes back to the numbers.

4 Likes

Missed this little issue, but you have to put this part of the code, where you check if the current index is not greater than the amount of the elements in the table, before setting .Text and running the updateHairSelection()

currentIndex.MaleHairs = currentIndex.MaleHairs + 1
HairChoiceLabel.Text = currentIndex.MaleHairs
updateHairSelection()

--this part:
if currentIndex.MaleHairs > #HairChoices.MaleHairs then
			
	currentIndex.MaleHairs = 1
			
end
6 Likes

That code was already there though.

6 Likes

One more thing!

Putting on an accessory isn’t working because you’re trying to look for a model in a model.

--this actually creates a model with an accessory inside of it:
local model = game:GetService("InsertService"):LoadAsset(currentHair)

--get accessory from the model:
local hair = model:FindFirstChildWhichIsA("Accessory")
--put it on your character
5 Likes

Yes, but you should put it above the lines with .Text and updateHairSelection.
Like this:

currentIndex.MaleHairs = currentIndex.MaleHairs + 1
if currentIndex.MaleHairs > #HairChoices.MaleHairs then	
	currentIndex.MaleHairs = 1
end

HairChoiceLabel.Text = HairChoices.MaleHairs[currentIndex.MaleHairs][1]
updateHairSelection()


5 Likes

It still does not insert the accessory,
image

local function updateHairSelection()
	
	if not ClickedMale and not ClickedFemale then return end
	print("Player has clicked one or the other")
	-- Update the hair selection based on the currentIndex
	local currentHair = nil
	
	if ClickedMale then
		print("player has clicked male, showing male hairs")
		
		currentHair = HairChoices.MaleHairs[currentIndex.MaleHairs][2]
		return currentHair
		
	elseif ClickedFemale then
		print("player has clicked female, showing female hairs")
		currentHair = HairChoices.FemaleHairs[currentIndex.FemaleHairs][2]
		return currentHair
	end
	
	-- Do something with the currentHair value, like updating an image or text label in the frame

	-- Insert the hair accessory on a dummy
	local model = game:GetService("InsertService"):LoadAsset(currentHair)
	
	local hair = model:FindFirstChildWhichIsA("Accessory")
	
	
	
	if hair then
		
		hair.Parent = CharDummy 
		
	end
	
	
	
end

Before you ask, yes chardummy is correct variable, as it works with other things such as the skin color that I have higher up in the script.

5 Likes

Another thing I’ve missed…

Remove these lines inside ifs:

return currentHair
return currentHair

They don’t let the code run further.

4 Likes

Error:

InsertService cannot be used to load assets from the client

I have no idea what to do now… because every player in the server shares the same rig, however the things applied are different on every client, I can’t do this on the server because of this…

3 Likes

Do not worry.

I think the easiest fix is actually creating all these models before hand and storing them somewhere.

Maybe like a global storage for both server and client. (so server can create a character later based on the client’s information)

So maybe making a table with names instead or IDs, whatever you like.

Let me show you an example:

local hairList = {
   'Afro',
   'Trecky Hair'
}

-- created a folder named storage in replicatedstorage.
local replicatedStorage = game:GetService('ReplicatedStorage')
local storage = replicatedStorage:WaitForchild('Storage')

-- getting the hair
local hairIndex = 1
local hair = hairList[hairIndex]

--putting the hair model on the character
local hairModel = storage[hair]:Clone()
hairModel.Parent = character

-- and then we could send the info to the server:
-- :FireServer({hair, skinColor}) -- hair name (or id), skin color
-- 'Afro Hair', 'Yes'

--but make sure to check ON THE SERVER if there's actually
-- a hair model and hair with a name like that.
4 Likes

So I’m on track with that, however it still needs to know if it has to look through the female hairs folder, or the males hair folder.

Script:

local function updateHairSelection()
	
	if not ClickedMale and not ClickedFemale then return end
	print("Player has clicked one or the other")
	-- Update the hair selection based on the currentIndex
	local currentHair = nil
	
	if ClickedMale then
		
		print("player has clicked male, showing male hairs")
		
		currentHair = HairChoices.MaleHairs[currentIndex.MaleHairs][2]
		
	elseif ClickedFemale then
		
		print("player has clicked female, showing female hairs")
		
		currentHair = HairChoices.FemaleHairs[currentIndex.FemaleHairs][2]
		
		
	end
	
	-- Do something with the currentHair value, like updating an image or text label in the frame

	-- Insert the hair accessory on a dummy
	local model = game:GetService("InsertService"):LoadAsset(currentHair)
	
	local hair = currentHair
	
	
	
	if hair then
		
		local HairModel = -- dont know what to put??
		
	end
	
	
	
end

Table:

local HairChoices = {
	
	MaleHairs = {
		
		"Bald",
		"Trecky Hair",
		"Afro",
		"Blowout",
		"Edgar Cut",
		"Medium Fade",
		"Middle Part",
		
	};
	FemaleHairs = {
		"Bald",
		"Popstar Hair",
		"Black Half Up Ponytails",
		"Wavy Side Part w/ Edges",
		"Pigtails",
		"Messy Buns",
		
	};
	
}

Folder Hiearchy:
image

Every single name of the accessories, matches the name of the table strings.

2 Likes

I was finally able to do it, however one problem arises.

The hair actually clones onto the player, there are no errors however the hair doesn’t appear. I checked the hiearchy of the character, and the accessory is there, with all of its descendents, but the hair doesn’t appear on the character.

image

Script:

local function updateHairSelection()
	
	local function RemoveHairs()
		
		for _,v in pairs(CharDummy:GetChildren()) do
			
			if v.ClassName == "Accessory" then
				v:Destroy()
			else
				print("There are no hairs to destroy")
			end
			
		end
	end
	
	
	if not ClickedMale and not ClickedFemale then return end
	print("Player has clicked one or the other")
	-- Update the hair selection based on the currentIndex
	local currentHair = nil
	
	
	
	local hair

	
	
	if ClickedMale then
		
		print("player has clicked male, showing male hairs")
		
		currentHair = HairChoices.MaleHairs[currentIndex.MaleHairsI]
		
		RemoveHairs()
		hair = MaleHairsFolder[currentHair]:Clone()
		hair.Parent = CharDummy
		
	elseif ClickedFemale then
		
		print("player has clicked female, showing female hairs")
		
		currentHair = HairChoices.FemaleHairs[currentIndex.FemaleHairsI]
		
		RemoveHairs()
		hair = FemaleHairsFolder[currentHair]:Clone()
		hair.Parent = CharDummy
	end
	
	-- Do something with the currentHair value, like updating an image or text label in the frame

	-- Insert the hair accessory on a dummy

	
	

		
		
		

	
	
	
end

The accessory is there, but it doesn’t go onto the character’s physical model.

2 Likes