Character Customization script doesn't detect the "Preview dummy" and\or accessories

I’m trying to make a Character customization system that allows you to preview the look you put together before firing the changes to all clients.

When you click on the “Customize” button, a LocalScript will create a copy of the LocalPlayer’s character, position it on top of a part, and at the same time move the LocalPlayer’s camera in front of the preview dummy.
A GUI with a TextBox area will show up, where you’ll be able to input the accessories, shirts, and pants’ IDs and these will load on the dummy (using insertService) to show how it looks.


My issue is: for some reason the script seems unable to either detect the Dummy or insert the items inputted via ID with the GUI.
The script gives no errors, so it’s much harder for me to figure out what the issue is…

local insertS = game:GetService("InsertService")
local player = game.Players.LocalPlayer

--Input ID
script.Parent.FocusLost:Connect(function()
	if tonumber(script.Parent.Text) then
		return tostring(script.Parent.Text)
	end
end)



--Insert accessory
	local number = tostring(script.Parent.Text)
	if not number then return end
	local asset
	local char = player.Character
	local dummy = game.Workspace:FindFirstChild("DummyPreview")
	local success = pcall(function()
		asset = insertS:LoadAsset(number)
	end)

	if not success then return end
	local child = asset:GetChildren()
	for _,v in pairs(child) do
		if v:IsA("Accessory") then
			dummy.Humanoid:AddAccessory(v)
		elseif v:IsA("Shirt") then
			local templ = v.ShirtTemplate
			dummy.Shirt.ShirtTemplate = templ
		elseif v:IsA("Pants") then
			local temp1 = v.PantsTemplate
			dummy.Pants.PantsTemplate = temp1
		elseif v:IsA("Decal") then
			dummy.Head:FindFirstChildOfClass("Decal").Texture = v.Texture
		end
	end
	asset:Destroy()

Apologies for the lack of information, I’ve been fighting with this problem for almost 2 days and nothing seems to work.

3 Likes

I think you’re being fooled a little bit by your “if not success then return end” which is throwing away the error that would be telling you InsertService cannot be used on the client.

image
The variable “asset” is empty before Inserting any instance…
Would :WaitForChild work?

How are you getting that error if LoadAsset never completes? Remove pcall() wrapped around LoadAsset and a new error will probably show itself.

Not sure, probably because the script runs as soon as the game loads, even if you didn’t click on the Customize button. The asset variable is empty before inserting any instance as I said, and the script immediately runs :GetChildren()

Done, no new errors… ^^"

You’re running it from a LocalScript. I get this error when I do that

image

This is my only source

local is = game:GetService("InsertService")
is:LoadAsset(3232453232)

So I’m lost on why you can’t seem to be able to replicate this error.

By the way, I forgot to mention the that the script in the first post was originally split in two, the first part (Input ID) was a LocalScript, while the second (Insert Accessory) was a ServerScript.

The script originally loaded the accessories directly on the Player’s character but I wanted to change it so that it’d display the changes on the dummy first. The server script, however, was unable to find the dummy since it was created locally, and I had no idea how to address it, so I ended up making the whole thing local. If it can help, here is the original script:

  • LocalScript
local event = workspace:WaitForChild("AvatarEvent")

script.Parent.FocusLost:Connect(function()
    if tonumber(script.Parent.Text) then
        event:FireServer(tostring(script.Parent.Text))
    end
end)
  • ServerScript
if not workspace:FindFirstChild("AvatarEvent") then
	local event = Instance.new("RemoteEvent")
	event.Name = "AvatarEvent"
	event.Parent = workspace
end
local event = workspace.AvatarEvent
local insertS = game:GetService("InsertService")

event.OnServerEvent:Connect(function(player, id)
	local number = tonumber(id)
	if not number then return end
	local asset
	local char = player.Character
	local success = pcall(function()
		asset = insertS:LoadAsset(number)
	end)
	
	if not success then return end
	local child = asset:GetChildren()
	for _,v in pairs(child) do
		if v:IsA("Accessory") then
			char.Humanoid:AddAccessory(v)
		elseif v:IsA("Shirt") then
			local templ = v.ShirtTemplate
			char.Shirt.ShirtTemplate = templ
		elseif v:IsA("Pants") then
			local temp1 = v.PantsTemplate
			char.Pants.PantsTemplate = temp1
		elseif v:IsA("Decal") then
			char.Head:FindFirstChildOfClass("Decal").Texture = v.Texture
		end
	end
	asset:Destroy()
end)

I’m really confused as well, I don’t recall InsertService being used only on Server :weary: in the previous message I specified that the script was originally a ServerScript indeed and I tried to make it Local for another issue I had.

Okay, this would have been nice to know.

So I’m really lost on what your actual setup is at this point. I’m not really going to take more shots in the dark. Here is a working setup.

In a server script, connected to the workspace.LoadAsset RemoteEvent
image

local event = workspace:WaitForChild("LoadAsset")
local iService = game:GetService("InsertService")

event.OnServerEvent:Connect(function(player, id)
	local char = player.Character
	local assetModel = iService:LoadAsset(id)
	for _,v in pairs(assetModel:GetChildren()) do
		if v:IsA("Accessory") then
			char.Humanoid:AddAccessory(v)
		elseif v:IsA("Shirt") then
			local templ = v.ShirtTemplate
			char.Shirt.ShirtTemplate = templ
		elseif v:IsA("Pants") then
			local temp1 = v.PantsTemplate
			char.Pants.PantsTemplate = temp1
		elseif v:IsA("Decal") then
			char.Head:FindFirstChildOfClass("Decal").Texture = v.Texture
		end
	end
	
	assetModel:Destroy()
end)

My local script I used to test it (yours will be different, because you’ll have it hooked to a UI)

wait(2)
workspace.LoadAsset:FireServer(1117747196)

Works fine. Shown below


Puts the vampire top hat on my character.

Yeah no, sorry but that doesn’t help much, besides maybe the neater code lol. You just did what the old code in post #7 did, load the assets directly on the Character.
What I understand is that InsertService doesn’t work AT ALL on Local, which is where the preview dummy is, only in the LocalPlayer’s screen.

The only thing that could work, at this point, is finding a way to make the ServerScript “aware” of the dummy, which is what I couldn’t figure out how to do and forced me to convert everything in a LocalScript.

Right, okay. That’s a bit of a different beast entirely. Humanoid:AddAccessory() doesn’t work locally and I tried digging around and simply can’t find anyone who has remade it.

I did about an hour of trial and error and it seems I’ve replicated it (for the most part, legacy support is not there).

Change the RemoteEvent I described to be a RemoteFunction. Use this as the server code

local event = workspace:WaitForChild("LoadAsset")
local iService = game:GetService("InsertService")

event.OnServerInvoke = function(player, id)
	local char = player.Character
	local assetModel = iService:LoadAsset(id)
	
	-- Force the asset to replicate
	assetModel.Parent = game.ReplicatedStorage
	
	return assetModel
end

And here is the LocalScript code I used to test it out. At the bottom is where you’d want to swap your own code in.

wait(1)
local character = game.Players.LocalPlayer.Character
character.Archivable = true
local charClone = character:Clone()
charClone.Parent = workspace

--[[
* AddAccessory does not work locally.
* We must replicate it here. Some functionality is not exact
* as, after much research, it doesn't seem like anyone has done this before?
* @param {Model} char
* @param {Accessory} a
* @returns {nil}
]]
local function addAccessoryLocally(char, a)
	local masterNumber = 0.625 -- This just seems to be an internal number for accessories? I guess?
	local weld = Instance.new("Weld")
	local attachmentToFind = a.Handle:FindFirstChildOfClass("Attachment")
	local attachmentInCharacter = char:FindFirstChild(attachmentToFind.Name, true)
	local yPosScale = char.Humanoid.HumanoidDescription.HeadScale
	local xAndZPosScale = attachmentInCharacter.Parent.Size.Z
	
	-- Adjust the mesh's scale
	local accessoryMesh = a.Handle:FindFirstChildWhichIsA("DataModelMesh")
	accessoryMesh.Scale = Vector3.new(masterNumber * xAndZPosScale, masterNumber * yPosScale, masterNumber * xAndZPosScale)
	
	-- The attachmentToFind has its position multiplied by the Accessory's change in scale.
	-- So if the new scale is 0.625 then 0.625 is multiplied against the attachment's Position
	attachmentToFind.Position = attachmentToFind.Position * accessoryMesh.Scale
	
	weld.Part0 = a.Handle
	weld.Part1 = attachmentInCharacter.Parent
	weld.C0 = attachmentToFind.CFrame
	weld.C1 = attachmentInCharacter.CFrame
	weld.Name = "AccessoryWeld"
	weld.Parent = a.Handle
	a.Parent = char
end

-- Test code
wait(2)
local assetFromServer = workspace.LoadAsset:InvokeServer(3916653334):Clone()
for _,v in pairs(assetFromServer:GetChildren()) do
	if v:IsA("Accessory") then
		addAccessoryLocally(charClone, v) -- Humanoid:AddAccessory does not work locally
	elseif v:IsA("Shirt") then
		local templ = v.ShirtTemplate
		charClone.Shirt.ShirtTemplate = templ
	elseif v:IsA("Pants") then
		local temp1 = v.PantsTemplate
		charClone.Pants.PantsTemplate = temp1
	elseif v:IsA("Decal") then
		charClone.Head:FindFirstChildOfClass("Decal").Texture = v.Texture
	end
end

Before accessory added (clone is on the left)
image

After accessory added locally

image

Side-by-side of me (on the right) which uses Roblox’s internal AddAccessory and the clone on the left that replicates it.

Edit: To get this working for all possible accessories it seems I would need to implement the accessory scaling as defined here: Rthro Scaling Specification

But unfortunately, that goes beyond the scope of what I would need to do. The original problem is solved, but you would need to implement custom scaling to properly get all accessories to look as they would on a standard Roblox humanoid.

1 Like

Is this script running on the Client? or in other words is this script written in a local script?

Try this module I made
NOTE: THIS SCRIPT WILL ONLY WORK IN A MODULE SCRIPT!!

local module = {}
local InsertService = game:GetService("InsertService")

--Input ID
function module.ParseId(Id) 
       self.Id = tonumber(Id)
       return self.Id
end

-- Sorting
function module:Sort(Item,ItemParent) 
      local Asset = self.Item or Item
      local Parent = self.Parent or ItemParent
      if Asset.ClassName == "Accessory" then
            if Parent:FindFirstChildOfClass("Humanoid") then
                 Parent:AddAccessory(Asset)
            end
      elseif Asset.ClassName == "Shirt" then
             if Parent:FindFirstChildOfClass("Shirt") then
                 Parent.ShirtTemplate = Asset.ShirtTemplate
            end
      elseif Asset.ClassName == "Pants" then
             if Parent:FindFirstChildOfClass("Pants") then
                 Parent.PantsTemplate = Asset.PantsTemplate
            end
      end
end

-- Input Item
function module:InsertComponent(Id,options) 
      local assetId = self.id or tonumber(Id)
      self.Item = InsertService:LoadAsset(assetId):GetChildren()[1]
       
     if self.Item then
          if options.Parent then
                 self.Parent = options.Parent
                 module:Sort()
          else
                return self.Item
          end
     end
end
1 Like

Hi, really sorry for taking so long to answer, I had a bit of a busy 2-days…
I used a portion of your code, the addAccessoryLocally function and it works great at welding the accessories, I just need to find a way to resize them accordingly.
Despite that, I managed to find another way to load shirts and pants and it works wonderfully too.
Thank you so much for your help and once again sorry for taking so long. x