Struggling with displaying player's character on viewportframe

I am trying to clone the player’s character to a viewport frame.

This was simple enough to do at first, but now the studio project is completely destroyed and everything is a buggy mess.

The approach is simple. Clone the player’s characterModel, remove everything that’s not a part or a folder in the model and then parent it to the worldmodel inside of the viewportframe.

The 3 issues are:
1: The rendering is inconsistent and glitchy. The arms are often missing from the rendering of the character.
image
2: Certain scrips are broken upon respawning, and there are hundreds of errors. I thought this would be fixed by deleting the scripts inside the clones, but apparently not.
3: The character rotation (from all axis) changes upon some respawns.
image

I know I post a lot here, but I have spend hours trying to make this work, and every time I try something it only breaks further :confused: Any help or guidance is appreciated.

-- LocalScript in StarterGui

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local playerListGui = script.Parent.playerList
local frame = playerListGui:WaitForChild("frame")
local textLabelTemplate = game.ReplicatedStorage:WaitForChild("TextLabel") -- Assuming you have a TextLabel template in ReplicatedStorage
local viewportFrameTemplate = game.ReplicatedStorage:WaitForChild("ViewportFrame") -- Assuming you have a ViewportFrame template in ReplicatedStorage

-- Function to clone the character model
local function cloneCharacterModel(player)
	local character = player.Character or player.CharacterAdded:Wait()
	if character then
		character.Archivable = true
		local clone = character:Clone()
		return clone
	end
end


-- Function to update the player list
local function updatePlayerList()
	-- Clear existing player labels
	for _, existingLabel in pairs(frame:GetChildren()) do
		if existingLabel:IsA("TextLabel") then
			existingLabel:Destroy()
		end
		if existingLabel:IsA("ViewportFrame") then
			existingLabel:Destroy()
		end
	end

	-- Create and position player labels
	local yOffset = 0.15
	local players = game.Players:GetPlayers()
	for i, player in pairs(players) do
		local playerLabel = textLabelTemplate:Clone()
		playerLabel.Text = player.Name
		playerLabel.Parent = frame
		
		local viewportFrame = viewportFrameTemplate:Clone()
		viewportFrame.Parent = frame
		
		local characterClone = cloneCharacterModel(player)
		if characterClone then
			local children = characterClone:GetChildren() 
			for index, child in pairs(children) do
				if not child:IsA("BasePart") or child:IsA("Folder") then
					child:Destroy()
				end
			end
			characterClone.Parent = viewportFrame.WorldModel
			characterClone:MoveTo(Vector3.new(1.5,-7,-4))
			characterClone.HumanoidRootPart.Anchored = true
		end

		if i == 1 then
			playerLabel.Position = UDim2.new(0.05, 0, 0.001, 0)
			viewportFrame.Position = UDim2.new(0.47, 0, 0, 0)
			for _, v in pairs(characterClone:GetChildren()) do
				v.Orientation = Vector3.new(0,-20,0)
			end
		else
			playerLabel.Position = UDim2.new(0.05, 0, yOffset, 0)
			viewportFrame.Position = UDim2.new(0.47, 0, yOffset, 0)
			yOffset = yOffset + playerLabel.Size.Y.Scale -- Use scale for vertical spacing
		end
	end
end

-- Initial update
updatePlayerList()

-- Connect to PlayerAdded and PlayerRemoving events
game.Players.PlayerAdded:Connect(updatePlayerList)
game.Players.PlayerRemoving:Connect(updatePlayerList)
character.wearing.ChildAdded:Connect(updatePlayerList)
character.wearing.ChildRemoved:Connect(updatePlayerList)

game:GetService("UserInputService").InputBegan:Connect(function(input, gameProcessedEvent)
	if input.KeyCode == Enum.KeyCode.Tab then
		if gameProcessedEvent == false then
			if frame.Visible == true then
				frame.Visible = false
			elseif frame.Visible == false then
				frame.Visible = true
			end
		end
	end
end)
2 Likes

I’m not thoroughly reviewing your script yet.
Could be many different approaches on how to do this, depending on the mechanics that your game has.
You could simply Clone() a blank default Rig, place it inside WorldModel into the ViewPort of the client, and set apply the HumanoidDescription of each player in game, instead of cloning the real Character model of each player and deleting the scripts in it.

You dont need to use MoveTo().
You can just change the CFrame of the model to 0,0,0, and have a camera aiming to that point


Is this really necessary? when you can use UI.layouts to arrange positions of the GUI elements?

if i == 1 then
			playerLabel.Position = UDim2.new(0.05, 0, 0.001, 0)
			viewportFrame.Position = UDim2.new(0.47, 0, 0, 0)
			for _, v in pairs(characterClone:GetChildren()) do
				v.Orientation = Vector3.new(0,-20,0)
			end
		else
			playerLabel.Position = UDim2.new(0.05, 0, yOffset, 0)
			viewportFrame.Position = UDim2.new(0.47, 0, yOffset, 0)
			yOffset = yOffset + playerLabel.Size.Y.Scale -- Use scale for vertical spacing
		end
1 Like

well that last part does work well and I prefer using scripting rather than UI elements as it is easier to understand.

I’m not familiar with applying HumanoidDescription. But I see how how the approach could solve the 2nd issue.

However when it comes to the camera, I never got it to work inside a viewportframe. It seems that it cannot find the model. Could you share what properties the camera would need for it to point at the proposed 0,0,0 model.

For the camera, as probably you already know, you should create a new camera from script, parent it to the viewport, should be scriptable, set its CFrame to 0,0,-5. That would make it go to the that point and a little offset of 5 or any value you prefer

1 Like

alright, will message back later with results (need a break from studio for a while)

1 Like

I’ve tried applying it here and it gives an error that humanoiddescription does not exist. It is quite odd as on the 2nd picture you can see that it exists. Using :WaitForChild() causes it to give a syntax error instead.

		local HumanoidDescription = char.Humanoid.HumanoidDescription
			characterClone.Humanoid.HumanoidDescription = HumanoidDescription
		end


image

edit: since the main appereance of the character is in the folder, now the folder from the player character copies into the r6 clone, and it does show in the r6 copy inside of the viewportframe. As you can see I put a part on my head inside of the folder - but it doesn’t show up on the playerList.
image

local function cloneCharacterModel(player)
	local character = game.ReplicatedStorage.r6model
	if character then
		character.Archivable = true
		local clone = character:Clone()
		
		local char = player.Character or player.CharacterAdded:Wait()	
		local appereance = char.wearing
		if clone.wearing then
			clone.wearing:Destroy()
		end
		local copy = appereance:Clone()
		copy.Parent = clone
		
		return clone
	end
end
		local characterClone = cloneCharacterModel(player)
		if characterClone then
			characterClone.Parent = viewportFrame.WorldModel
			characterClone:MoveTo(Vector3.new(2,-7,-4))
--			local HumanoidDescription = char.Humanoid.HumanoidDescription
--			characterClone.Humanoid.HumanoidDescription = HumanoidDescription

edit 2: I also tried creating a rig with the function in the documentation, however the humanoidDescription didn’t do anything to the rig. It’s just a blank and grey r6 model.

local char = player.Character or player.CharacterAdded:Wait()
	local HumanoidDesc = char.Humanoid.HumanoidDescription
	local clone = game.Players:CreateHumanoidModelFromDescription(HumanoidDesc, Enum.HumanoidRigType.R6)

Idk how to approach this anymore :confused:

edit 3: Using “ApplyDescription” causes the error Humanoid::ApplyDescription() DataModel was not available

i would recommend u to use a vector3 value so u can actually find the perfect position for the rig to fit in during runtime. When u get the perfect vector position, just apply it in the script

well yes the position and angle are correct with using the r6 rig as the clone. The issue I have now is that the clone needs to change appereance based on the player’s character appereance, and all the methods I have been able to find are returning errors and blank rigs.

SOLUTION is found. Turns out that because it is a character, you can just use the HumanoidRootPart as a CFrame of the model, so you avoid deprecated methods. Very simple fix.

It may still have some bugs related to variable overwriting caused by script duplication, but it can be solved with a cloning loop if nessecary.

local function cloneCharacterModel(player)
	local character = player.Character or player.CharacterAdded:Wait()
	if character then
		character.Archivable = true
		local clone = character:Clone()
		return clone
	end
end
local characterClone = cloneCharacterModel(player)
		if characterClone then
			local children = characterClone:GetChildren() 
			for index, child in pairs(children) do
				if child:IsA("LocalScript") then
					child:Destroy()
				end
			end
			characterClone.Parent = viewportFrame.WorldModel
			characterClone.HumanoidRootPart.CFrame = CFrame.new(2, -2, -4, 0, 10, 0, 180)
		end
3 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.