Optimizing Code for a player model in a Viewport

So, basically, I wrote some code that allows for a UI to replicate the player’s character model. Here’s the code in question:

rs = game:GetService("RunService")
viewport = script.Parent.Viewport
world = viewport.WorldModel

rs.RenderStepped:Connect(function()
	if world:FindFirstChild("Model") then
		world.Model:Destroy()
	end
	
	local char = Instance.new("Model")
	char.Parent = world
	
	for i, child in ipairs(game.Players.LocalPlayer.Character:GetChildren()) do
		child:Clone().Parent = char
	end
	
	char.PrimaryPart = char.HumanoidRootPart
	
	for i, child in ipairs(char:GetChildren()) do
		if child ~= char.PrimaryPart and child:IsA("BasePart") then
			local weld = Instance.new("WeldConstraint")
			weld.Parent = char.PrimaryPart
			weld.Part0 = char.PrimaryPart
			weld.Part1 = child
		elseif child:IsA("Tool") or child:IsA("Accessory") then
			local weld = Instance.new("WeldConstraint")
			weld.Parent = char.PrimaryPart
			weld.Part0 = char.PrimaryPart
			weld.Part1 = child.Handle
		end
	end
	
	char.PrimaryPart.CFrame = CFrame.new(char.PrimaryPart.CFrame.Position) * CFrame.Angles(0,math.rad(-80),0)
	
	local cam = Instance.new("Camera")
	cam.Parent = world
	cam.FieldOfView = 30
	
	viewport.CurrentCamera = cam
	
	cam.CFrame = CFrame.new(char.PrimaryPart.Position + Vector3.new(9,2,0), char.PrimaryPart.Position)
	cam.CFrame *= CFrame.Angles(0,0,math.rad(-45))
end)

Here’s the hierarchy of the UI (the only important parts are all the stuff that’s in “PlayerDisplay”):
Screen Shot 2022-09-30 at 10.13.06 PM
This code works almost perfectly. It clones the character and puts it in a UI, just like how I want it! The only catch is that-


it runs like bricks.
Any help improving this is appreciated!

4 Likes

I don’t see the problem, it looks good.

1 Like

Screen Shot 2022-09-30 at 10.29.55 PM
I don’t think this is normal.

O_O

How about… just clone the player once and use the same animations playing for the original character?

Be right back, imma go rest eyes before I explode lol.

Okay, i’m back.

1 Like

I’ll try that and see if it improves performance.

1 Like

Cloning the character every frame is less than ideal, you could very easily detect things like if the character is walking or jumping via the UI code and then play the animation on a single clone of the character.

for walking:

    if humanoid.MoveDirection.Magnitude > 0 then

and for jumping, I think this will work (although I have never really done this before):

humanoid:GetPropertyChangedSignal("Jump"):Connect(function()
    print(humanoid.Jump)
end)

also there is UserInputService.JumpRequest but that fires multiple times for a single jump so you’ll need a debounce

2 Likes

This is great for a game with the simplest controls, but my game is gonna have a lot more than the built in animations, so I’m gonna go with @SirHoog 's idea of updating the animation every time the one’s that playing changes.

uhhh


Yeah I might need a bit of help with this one

rs = game:GetService("RunService")

print("creating model")

charmodel = Instance.new("Model")
charmodel.Parent = script.Parent.Viewport.WorldModel
charmodel.Name = "Character"

function getchildren()
	task.wait()
	local success, em = pcall(function()
		for i, child in ipairs(game.Players.LocalPlayer.Character:GetChildren()) do
			local newpart = child:Clone()
			newpart.Parent = charmodel
		end
	end)
	if not success then
		wait(em)
		charmodel:ClearAllChildren()
		getchildren()
	end
	charmodel.PrimaryPart = charmodel.HumanoidRootPart
	charmodel.PrimaryPart.CFrame = CFrame.new(0,0,0)
end

getchildren()

cam = Instance.new("Camera")
cam.Parent = script.Parent.Viewport.WorldModel
cam.CFrame = CFrame.new(Vector3.new(9,2,0), charmodel.PrimaryPart.Position)
cam.FieldOfView = 70
cam.CameraSubject = charmodel

script.Parent.Viewport.CurrentCamera = cam

oldtracks = nil

rs.RenderStepped:Connect(function()
	cam.CFrame = CFrame.new(game.Players.LocalPlayer.Character.PrimaryPart.Position + Vector3.new(9,2,0), charmodel.PrimaryPart.Position)
	if oldtracks ~= game.Players.LocalPlayer.Character.Humanoid.Animator:GetPlayingAnimationTracks() then
		for i, track in ipairs(charmodel.Humanoid.Animator:GetPlayingAnimationTracks()) do
			track:Stop()
		end
		charmodel.Humanoid.Animator:LoadAnimation(game.Players.LocalPlayer.Character.Humanoid.Animator:GetPlayingAnimationTracks()[1].Animation):Play()
	end
	
	oldtracks = game.Players.LocalPlayer.Character.Humanoid.Animator:GetPlayingAnimationTracks()
end)
1 Like

Try rotating the UI 90 degrees (or the character)

Yeah, I know how to fix that, but it’s everything else about it.

  1. The camera isn’t looking at the model despite what the code should do.
  2. The character is offset from the middle.
  3. The character isn’t even playing the right animation. Or playing an animation at all.
1 Like

Can you send me your current script?

Don’t worry about that script. I started over and rewrote the script, and I’ve fixed two of the three issues.

task.wait()

rs = game:GetService("RunService")

charmodel = script.Parent.Viewport.WorldModel.Character

cam = Instance.new("Camera")
cam.Parent = charmodel.Parent
charmodel.Parent.Parent.CurrentCamera = cam
cam.FieldOfView = 30

oldtracks = nil

rs.RenderStepped:Connect(function()
	print(oldtracks)
	cam.CFrame = CFrame.new(-1,0,-10) * CFrame.Angles(math.rad(-180),math.rad(-5),math.rad(135))
	if oldtracks ~= game.Players.LocalPlayer.Character.Humanoid.Animator:GetPlayingAnimationTracks() then
		for i, track in ipairs(charmodel.Humanoid.Animator:GetPlayingAnimationTracks()) do
			track:Stop()
		end
		charmodel.Humanoid.Animator:LoadAnimation(game.Players.LocalPlayer.Character.Humanoid.Animator:GetPlayingAnimationTracks()[1].Animation):Play()
	end

	oldtracks = game.Players.LocalPlayer.Character.Humanoid.Animator:GetPlayingAnimationTracks()
end)

When an animation starts playing, after a bit of time it’ll just… stop. Like it’ll stop playing until a new animation starts playing, and it’ll do the same thing. It’s strange.

1 Like

Why don’t you just entirely remove the if statement and the for loop and just load the animation? Won’t it override the old track?

1 Like

I’m checking to see if the animations being played are the same because when you constantly send AnimationPlay requests it screws things up and is not great for performance, therefore putting me back at where I was at at the beginning.

1 Like

I’m almost there! I managed to get the player to play one animation at a time.
I should be done soon, thanks a ton for your help!

1 Like

I’m gonna hit the sack, but I’ll post the finalized script tomorrow in case anybody needs it. You never know when someone’ll stumble upon this post 3 years later.

2 Likes

I’m almost finished, but there’s one thing still bugging me.
I tried to implement a system where the tool’s in the player’s character would clone over to the preview of the character in the box, and while the tool does go in the preview, it’s heavily offset, and its tool grips are being ignored by the player.
Any help with this?

task.wait()

rs = game:GetService("RunService")

charmodel = script.Parent.Viewport.WorldModel.Character

cam = Instance.new("Camera")
cam.Parent = charmodel.Parent
charmodel.Parent.Parent.CurrentCamera = cam
cam.FieldOfView = 30

oldtracks = nil

rs.RenderStepped:Connect(function()
	cam.CFrame = CFrame.new(-1,0,-10) * CFrame.Angles(math.rad(-180),math.rad(-5),math.rad(135))
	
	oldtracks = game.Players.LocalPlayer.Character.Humanoid.Animator:GetPlayingAnimationTracks()
	
	if charmodel:FindFirstChildWhichIsA("Tool") ~= nil then -- clone tools
		if game.Players.LocalPlayer.Character:FindFirstChildWhichIsA("Tool") ~= nil then
			if charmodel:FindFirstChildWhichIsA("Tool").Name ~= game.Players.LocalPlayer.Character:FindFirstChildWhichIsA("Tool").Name then
				charmodel:FindFirstChildWhichIsA("Tool"):Destroy()
				game.Players.LocalPlayer.Character:FindFirstChildWhichIsA("Tool"):Clone().Parent = charmodel
			end
		else
			charmodel:FindFirstChildWhichIsA("Tool"):Destroy()
		end
	elseif game.Players.LocalPlayer.Character:FindFirstChildWhichIsA("Tool") ~= nil then
		game.Players.LocalPlayer.Character:FindFirstChildWhichIsA("Tool"):Clone().Parent = charmodel
	end
	
	if oldtracks ~= game.Players.LocalPlayer.Character.Humanoid.Animator:GetPlayingAnimationTracks() then
		for i, track in ipairs(charmodel.Humanoid.Animator:GetPlayingAnimationTracks()) do
			local found = false
			
			for i, subtrack in ipairs(oldtracks) do
				if subtrack.Animation.AnimationId == track.Animation.AnimationId then
					found = true
				end
			end
			
			if found == false then
				track:Stop()
			end
		end
		
		for i, track in ipairs(game.Players.LocalPlayer.Character.Humanoid.Animator:GetPlayingAnimationTracks()) do
			local playingtracks = charmodel.Humanoid.Animator:GetPlayingAnimationTracks()
			
			local found = false
			
			for a, subtrack in ipairs(playingtracks) do
				if subtrack.Animation.AnimationId == track.Animation.AnimationId then
					found = true
				end
			end
			
			if not found then
				charmodel.Humanoid.Animator:LoadAnimation(game.Players.LocalPlayer.Character.Humanoid.Animator:GetPlayingAnimationTracks()[i].Animation):Play()
			end
		end
		
		local playingtracks = {}
		
		for i, track in ipairs(charmodel.Humanoid.Animator:GetPlayingAnimationTracks()) do -- removes excess tracks playing
			if table.find(playingtracks, track.Animation.AnimationId) then
				track:Stop()
			else
				table.insert(playingtracks, track.Animation.AnimationId)
			end
		end
	end
end)

I have one of these character viewport sort of things, but no idea how to read the memory thing lol.


is this okay?

Not sure why it isn’t covering as much but there you go.
The code I used:

char = game.Players.LocalPlayer.Character
cam = Instance.new("Camera",script.Parent)

game:GetService("RunService").RenderStepped:Connect(function()
	for i,v in pairs(script.Parent.ViewportFrame.Character1:GetDescendants()) do
		v:Destroy()
	end
	
	for i,v in pairs(char:GetChildren()) do
		local v2 = v:Clone()
		
		if v2:IsA("Script") or v2:IsA("LocalScript") or v2:IsA("ModuleScript") then
			v2:Destroy()
		else
			v2.Parent = script.Parent.ViewportFrame.Character1
			if v2:IsA("Humanoid") then
				v2.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
			end
		end
	end
	
	script.Parent.ViewportFrame.CurrentCamera = cam
	cam.CFrame = game.Players.LocalPlayer.Character.HumanoidRootPart.CFrame * CFrame.new(-2,2,-5) * CFrame.Angles(math.rad(5),math.rad(200),math.rad(20))
end)

(I think it’s a terrible free model I got a year ago actually…)