First and foremost, I would appreciate if anyone could assist me in figuring out a way to better do this, as I believe my approach is kind of nasty and would like to improve my code going forward.
The Purpose of the code: To reference a Dictionary with userdata and take the "Race Name " and find that specific folder in replicatedstorage which contains the cosmetic info, to do the same except for the RaceVariant which is in the same location under that race folder, once these two values are selected the body should be recoloured appropriately.
THE ERROR: Oftentimes the player will spawn without being recoloured, I imagine this is due to how I am trying to select the cosmetic info and the timing before applying it. I’m not a very experienced scripter so advice on how to improve this code massively would be appreciated as I am unsure how else to solve this issue.
This isn’t really a reason I cannot store the information for the cosmetic info in the datastore, I just imagine replicatedstorage would be better for reference from both the client and server.
My Code:
local CharacterHandler = {}
local RaceHandler = require(script.Parent.RaceHandler)
local ReplicatdStorage = game:GetService("ReplicatedStorage")
function CharacterHandler:LoadChar(Player, DataTable, Character)
local UUID = Player.UserId
DataTable[UUID]["CharacterData"]["Race"].RaceVar = 1
local Race = DataTable[UUID]["CharacterData"]["Race"].RaceType
local RaceVar = DataTable[UUID]["CharacterData"]["Race"].RaceVar
local Humanoid = Character:WaitForChild("Humanoid")
local SelectedSkinColour
local SelectedEyeColour
local SelectedHairColour
for i, v in pairs(ReplicatdStorage.RaceAssets:GetChildren()) do
if Race == v.Name then
for j, k in pairs(v:GetChildren()) do
if k:FindFirstChild("VariantNum") then
if k:FindFirstChild("VariantNum").Value == RaceVar then
SelectedSkinColour = k:FindFirstChild("SkinColour")
SelectedEyeColour = k:FindFirstChild("EyeColour")
SelectedHairColour = k:FindFirstChild("HairColour")
end
end
end
end
end
local connection = Player.CharacterAppearanceLoaded:Connect(function(character)
local humanoid = character:FindFirstChildOfClass("Humanoid")
humanoid:RemoveAccessories()
local Head = character:WaitForChild("Head")
local Left_Arm = character:WaitForChild("Left Arm")
local Right_Arm = character:WaitForChild("Right Arm")
local Torso = character:WaitForChild("Torso")
local Left_Leg = character:WaitForChild("Left Leg")
local Right_Leg = character:WaitForChild("Right Leg")
Head.Color = SelectedSkinColour.Value
Left_Arm.Color = SelectedSkinColour.Value
Right_Arm.Color = SelectedSkinColour.Value
Torso.Color = SelectedSkinColour.Value
Left_Leg.Color = SelectedSkinColour.Value
Right_Leg.Color = SelectedSkinColour.Value
end)
end
return CharacterHandler
I would appreciate suggestions for improving this code a lot. Thank you
Hi! I suppose the main issue might simply be how exactly your trying to update the character. I cannot answer on the basis of efficiency or optimization, as I have not made a comparison, however I prefer to use models instead of values. This is simply because it is much easier for me to load everything all at once than each individual part at a time. The only time I would use values to change things would be skins, but even there I simply use models as it is much more comfortable for me.
So I will make this post try to do 2 things:
a. Give advice on your code and hopefully resolve the issue given the current method
b. Share with you my method for loading in custom characters
Current Code
Ok so far, getting the CharacterData, specifically the “RaceType” value is a good start, as that is your identifier to call specific parts of your ReplicatedStorage. I will agree that perhaps the main issue might be the code.
local CharacterHandler = {}
local RaceHandler = require(script.Parent.RaceHandler)
local ReplicatdStorage = game:GetService("ReplicatedStorage")
function CharacterHandler:LoadChar(Player, DataTable, Character)
-- I assume you correctly got the data
--To get the character, I believe you can do local character = Player.Character,
--but if you are getting the character, that is fine
local UUID = Player.UserId
DataTable[UUID]["CharacterData"]["Race"].RaceVar = 1
local Race = DataTable[UUID]["CharacterData"]["Race"].RaceType
local RaceVar = DataTable[UUID]["CharacterData"]["Race"].RaceVar
local Humanoid = Character:WaitForChild("Humanoid")
local SelectedSkinColour
local SelectedEyeColour
local SelectedHairColour
--I will assume that there are no issues when you call
for i, v in pairs(ReplicatdStorage.RaceAssets:GetChildren()) do
if tostring(Race) == tostring(v.Name) then
for j, k in pairs(v:GetChildren()) do
if k:FindFirstChild("VariantNum") then
if k:FindFirstChild("VariantNum").Value == RaceVar then
SelectedSkinColour = k:FindFirstChild("SkinColour")
SelectedEyeColour = k:FindFirstChild("EyeColour")
SelectedHairColour = k:FindFirstChild("HairColour")
else
print("The values do not match")
end
else
print("Could not find the VariantNum child")
end
end
else
print("The values do not match")
end
end
--This is the confusing part for me to understand, why exactly have local connection here inside your module?
--Remember you need to actually call this function as it is right now idle in your module script, you need to separately call it
--This is because you do define it, but there seems to be no place you actually use it, so its just better to do Player.CharacterAppearanceLoaded:Connect()
--
local connection = Player.CharacterAppearanceLoaded:Connect(function(character)
local humanoid = character:FindFirstChildOfClass("Humanoid")
humanoid:RemoveAccessories()
local Head = character:WaitForChild("Head")
local Left_Arm = character:WaitForChild("Left Arm")
local Right_Arm = character:WaitForChild("Right Arm")
local Torso = character:WaitForChild("Torso")
local Left_Leg = character:WaitForChild("Left Leg")
local Right_Leg = character:WaitForChild("Right Leg")
Head.Color = SelectedSkinColour.Value
Left_Arm.Color = SelectedSkinColour.Value
Right_Arm.Color = SelectedSkinColour.Value
Torso.Color = SelectedSkinColour.Value
Left_Leg.Color = SelectedSkinColour.Value
Right_Leg.Color = SelectedSkinColour.Value
end)
end
return CharacterHandler
Method I use
The method I use generally revolves around 2 things, we get the Player.Character and each “skin” (basically model) has the exact same type of children (like humanoid, parts, welds, etc.). Here is how it goes:
Get the Player Data - I won’t go into this as you seem to have a good grasp on calling the player data, we just need the name of the skin
Clone the model from replicated storage - this I would say is the most important part as when you clone, you can change the clone.Parent to the Player.Character, allowing you to have an actual model as the character. So now when you load in, your character will load (I have yet to experience issues with this method).
This is incorrect, the function is being activated correctly and I can provide a server script example if needed, but it would just show passing the datatable in the screenshot and the player / character object.
Sure! I will provide you a general step by step framework that you should be able to easily customize:
Step 1: Create your models/characters that you want the player to “dress up” as and put them in Replicated Storage (additionally, if you like you can put them under a folder, if you would like to stay organized. It should look something like this:
Step 2: Create a Script in ServerScriptService, you can call this CharacterLoader. IMPORTANT: You will need a remote or bindable event to indicate the Server of when you want to load the character or when the character. For me, when the player loads, I fire a bindable event to the CharacterLoader script so it can get ready. I’m not the biggest fan when it comes down to client firing remotes, so I just load it beforehand, unless the player is changing their skin (like Arsenal). Basically you need a remote event, to fire to CharacterLoader
Assume that the Data for the player is structured like this (I believe you already have a structure):
Data = {
PlayerName = "RealCalculus"
Character = "Alpha"
}
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LoadingCharacter = ReplicatedStorage["Events"]:WaitForChild("LoadingCharacter")
--Now we need to handle the event logic (I used a bindable, my DataStorage will send
--the player data to this script
LoadingCharacter.Event:Connect(function(player, Data)
local skin = Data.Character
local characterObject = ReplicatedStorage["Skins"]:WaitForChild(skin):Clone()
characterObject.Parent = workspace
player.Character = characterObject
player:LoadCharacter()
end)
This is about the general idea of creating your skin system, I might have a couple errors in terms of defining terms but this is the general gist. I hope this helps!