Character cloning incorrectly

i’ve figured out how to create 2.5D characters using viewport frames, which is great! now i’ve managed to get myself into a pickle: every time i respawn, my character gets cloned incorrectly, which makes me into a gray default dummy, and my accessories aren’t being removed. i’ve been trying to solve this for a while, but i’ve had no luck.

the place file, in case my spaghetti code is confusing

pseudo 3D.rbxl (66.7 KB)

my full script
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

--\\ character setup
local client = Players.LocalPlayer
local plrGui = client:WaitForChild("PlayerGui")
local spriteSheet = plrGui:WaitForChild("char-2d")
local viewport = spriteSheet:FindFirstChildWhichIsA("ViewportFrame")
local worldModel = viewport:FindFirstChildWhichIsA("WorldModel")
local fakeChar = viewport:FindFirstChild("noob")
local currentCam = workspace.CurrentCamera
local testPart = workspace:WaitForChild("test-part") -- fake sprite for testing purposes (NOT real)
local connection

--\\ debugging function to track issues
local function debugPrint(message)
	print(`[DEBUG:] {tostring(message)}`)
end

--\\ other functions
local function applyCharacter(char)
	debugPrint("applying character...")

	if not char.Archivable then
		debugPrint("original character is not archivable")
		char.Archivable = true
	end

	local newChar = char:Clone()

	if not newChar then
		debugPrint("new character is nil after cloning")
		return
	end

	newChar.Parent = worldModel

	if not fakeChar.PrimaryPart then
		debugPrint("fake character's primary part is nil")
		return
	end

	newChar:PivotTo(fakeChar.PrimaryPart.CFrame)
	debugPrint("character applied successfully")
end

local function makeItemInvisible(target)
	for _, descendant in target:GetDescendants() do
		if descendant:IsA("BasePart") or descendant:IsA("MeshPart") then
			descendant.Transparency = 1
			descendant.Material = Enum.Material.SmoothPlastic
		elseif descendant:IsA("Decal") then
			descendant.Transparency = 1
		elseif descendant:IsA("Accessory") then
			descendant:Destroy()
		end
	end
end

local function loadCharacter(char)
	if not char or not char.Parent then
		debugPrint("character is nil or not fully spawned")
		return
	end

	debugPrint("loading character...")
	applyCharacter(char)

	-- disconnect existing connection if character respawns
	if connection then 
		connection:Disconnect() 
	end

	local root = char:WaitForChild("HumanoidRootPart", 10)
	if not root then
		debugPrint("error: root not found")
		return
	end

	makeItemInvisible(char) -- make character invisible

	-- sync y-axis rotation with test part
	connection = RunService.PreRender:Connect(function()
		local camCFrame = currentCam.CFrame
		local _, camYRot, _ = camCFrame:ToEulerAnglesYXZ() -- gimme that y-axis rotation
		local partPos = root.Position

		-- apply y-axis of the camera to the part while maintaining its position
		testPart.CFrame = CFrame.new(partPos) * CFrame.Angles(0, camYRot, 0)
	end)
	debugPrint("character loaded successfully")
end

--\\ listen for character spawns and load the character
client.CharacterAdded:Connect(function(newCharacter)
	debugPrint("character added event triggered")
	newCharacter:WaitForChild("HumanoidRootPart", 10)
	loadCharacter(newCharacter)
end)

-- load the character if already present (first run)
if client.Character and client.Character.Parent then
	debugPrint("character exists on first run")
	client.Character:WaitForChild("HumanoidRootPart", 10)
	loadCharacter(client.Character)
else
	debugPrint("no character on first run")
end

here is a screenshot of what happens after i respawn.


vs a screenshot of what is supposed to happen.

I’d suggest having a dummy for the 2.5D effect without cloning the character and when the player loads you just apply the user’s Humanoid Description onto the dummy.

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local client = Players.LocalPlayer
local plrGui = client:WaitForChild("PlayerGui")
local spriteSheet = plrGui:WaitForChild("char-2d")
local viewport = spriteSheet:FindFirstChildWhichIsA("ViewportFrame")
local worldModel = viewport:FindFirstChildWhichIsA("WorldModel")
local fakeChar = viewport:FindFirstChild("noob")
local currentCam = workspace.CurrentCamera
local testPart = workspace:WaitForChild("test-part")
local connection

local function debugPrint(message)
	print(`[DEBUG:] {tostring(message)}`)
end

local function applyCharacter(char)
	debugPrint("applying character...")

	local newChar = char:Clone()
	if not newChar then
		debugPrint("new character is nil after cloning")
		return
	end

	newChar.Parent = worldModel
	if not fakeChar.PrimaryPart then
		debugPrint("fake character's primary part is nil")
		return
	end

	newChar:PivotTo(fakeChar.PrimaryPart.CFrame)
	debugPrint("character applied successfully")
end

local function clearAccessories(char)
	for _, accessory in ipairs(char:GetChildren()) do
		if accessory:IsA("Accessory") then
			accessory:Destroy()
		end
	end
end

local function loadCharacter(char)
	if not char or not char.Parent then
		debugPrint("character is nil or not fully spawned")
		return
	end

	debugPrint("loading character...")
	clearAccessories(char) -- Clear accessories before applying the new character
	applyCharacter(char)

	if connection then 
		connection:Disconnect() 
	end

	local root = char:WaitForChild("HumanoidRootPart", 10)
	if not root then
		debugPrint("error: root not found")
		return
	end

	-- Sync y-axis rotation with test part
	connection = RunService.PreRender:Connect(function()
		local camCFrame = currentCam.CFrame
		local _, camYRot, _ = camCFrame:ToEulerAnglesYXZ()
		local partPos = root.Position
		testPart.CFrame = CFrame.new(partPos) * CFrame.Angles(0, camYRot, 0)
	end)
	debugPrint("character loaded successfully")
end

client.CharacterAdded:Connect(function(newCharacter)
	debugPrint("character added event triggered")
	newCharacter:WaitForChild("HumanoidRootPart", 10)
	loadCharacter(newCharacter)
end)

if client.Character and client.Character.Parent then
	debugPrint("character exists on first run")
	client.Character:WaitForChild("HumanoidRootPart", 10)
	loadCharacter(client.Character)
else
	debugPrint("no character on first run")
end