Character Animation ViewportFrame

Problem Replicating Animations in a ViewPortFrame in Roblox Studio

In my project, I needed to replicate the player’s animations inside a ViewportFrame so that actions like walking or jumping would be reflected without causing the camera to go out of alignment. However, I found that the camera wasn’t correctly following the player, and the animations weren’t updating in real time. Below, I will explain how I resolved this issue.


Step-by-Step Process

1. Cloning the Character: First, I created a function that clones only the visible and relevant parts of the character, excluding scripts and other unnecessary elements.

local player = game.Players.LocalPlayer
local viewportCamera = Instance.new("Camera")

local function CloneCharacter(char)
    local newModelCharacter = Instance.new("Model")

    for i, part in pairs(char:GetChildren()) do
        if not part:IsA("BaseScript") then
            local newPart = part:Clone()
            newPart.Parent = newModelCharacter
        end
    end

    return newModelCharacter
end

2. Adding the Clone to the WorldModel: Once the character was cloned, I added it to the WorldModel inside the ViewportFrame.

local PlayerCharacter = CloneCharacter(player.Character)
PlayerCharacter.Parent = WorldModel

3. Configuring the Camera: I created a camera for the ViewportFrame and positioned it correctly to display the character at a suitable distance.

VPF.CurrentCamera = viewportCamera
viewportCamera.Parent = VPF
viewportCamera.CFrame = CFrame.new(PlayerCharacter.HumanoidRootPart.Position - Vector3.new(0, 0, 8)) 
                          * CFrame.Angles(0, math.rad(180), 0)

At this point, the character has been cloned, added to the WorldModel, and the camera is properly set up. Now, we can see what the ViewportFrame looks like with the cloned character but without animations, i.e., just in a static view.

Screenshot 2025-01-16 235026


Code up to this Point:

local player = game.Players.LocalPlayer
local viewportCamera = Instance.new("Camera")

local function CloneCharacter(char)
    local newModelCharacter = Instance.new("Model")

    for i, part in pairs(char:GetChildren()) do
        if not part:IsA("BaseScript") then
            local newPart = part:Clone()
            newPart.Parent = newModelCharacter
        end
    end

    return newModelCharacter
end

local function UpdateCharacterView()
        local VPF: ViewportFrame = player.PlayerGui.Inventory.Content.EquipmentContent.CharacterView
        local WM: WorldModel = VPF.WorldModel
        WM:ClearAllChildren()

        local PlayerCharacter = CloneCharacter(player.Character)
        PlayerCharacter.Parent = WM

        VPF.CurrentCamera = viewportCamera
        viewportCamera.Parent = VPF
        viewportCamera.CFrame = CFrame.new(PlayerCharacter.HumanoidRootPart.Position - Vector3.new(0, 0, 5)) 
                                  * CFrame.Angles(0, math.rad(180), 0) 
end

player.CharacterAdded:Connect(function()
    UpdateCharacterView()
end)

4. Implementing Recursion: Now, to make the player’s animations update in real time, I implemented a recursive function that continuously updates the character inside the ViewportFrame.

local function UpdateCharacterView()
    task.spawn(function()
        local VPF: ViewportFrame = player.PlayerGui.Inventory.Content.EquipmentContent.CharacterView
        local WM: WorldModel = VPF.WorldModel
        WM:ClearAllChildren()

        local PlayerCharacter = CloneCharacter(player.Character)
        PlayerCharacter.Parent = WM

        VPF.CurrentCamera = viewportCamera
        viewportCamera.Parent = VPF
        viewportCamera.CFrame = CFrame.new(PlayerCharacter.HumanoidRootPart.Position - Vector3.new(0, 0, 5)) 
                                  * CFrame.Angles(0, math.rad(180), 0)

        task.wait()
        UpdateCharacterView() -- Recursively call the function after waiting
    end)
end

Full Code

Here is the complete code I implemented:

local player = game.Players.LocalPlayer
local viewportCamera = Instance.new("Camera")

local function CloneCharacter(char)
    local newModelCharacter = Instance.new("Model")

    for i, part in pairs(char:GetChildren()) do
        if not part:IsA("BaseScript") then
            local newPart = part:Clone()
            newPart.Parent = newModelCharacter
        end
    end

    return newModelCharacter
end

local function UpdateCharacterView()
    task.spawn(function()
        local VPF: ViewportFrame = player.PlayerGui.Inventory.Content.EquipmentContent.CharacterView
        local WM: WorldModel = VPF.WorldModel
        WM:ClearAllChildren()

        local PlayerCharacter = CloneCharacter(player.Character)
        PlayerCharacter.Parent = WM

        VPF.CurrentCamera = viewportCamera
        viewportCamera.Parent = VPF
        viewportCamera.CFrame = CFrame.new(PlayerCharacter.HumanoidRootPart.Position - Vector3.new(0, 0, 5)) 
                                  * CFrame.Angles(0, math.rad(180), 0)

        task.wait()
        UpdateCharacterView()
    end)
end

player.CharacterAdded:Connect(function()
    UpdateCharacterView()
end)

Outcome

With this implementation, I was able to replicate the player’s animations inside a ViewportFrame.

Final Evidence: Here is the video showing the movement and animations inside the ViewportFrame in real-time.


Feedback

If anyone has a more efficient way to achieve this result, I would appreciate any suggestions or improvements. I hope this helps those who are trying to solve the same problem.

Deleting the Character and cloning it back every single frame? With a recursive function!?
Thats a big no, first of all, your function most likely will crash, due to the stack overflow errors.
Second, cloning its a very expensive operation, especially when done every single frame.
Third, You added task.spawn(), why? You intentionally adding race condition problems for yourself.

There is much better ways to achieve your goal.
For example:
Run a loop to update characters, Motor6D’s/Welds every frame (In case you asking how to deal with new added items to the character, you can always use events, to see if something new has been added to/removed from character, AND ONLY then you can delete the old one, and paste the new one in the Viewport (Or even better, add/remove the object itself, not the whole character in the viewport) and update the new Welds respectfully.

  1. Clone it once instead of cloning and deleting it WITH RECURSIVE! jeez~ :skull:
  2. Once you cloned the character, on every frame, loop through your character part and set the cloned part matches the character part CFrame.

:tada: And there you go, by setting the clonedCharacter’s parts CFrame matching the character’s parts CFrame, you now have almost no latency character viewport frame. :tada: