Player Model Viewport. How can I make this script more optimized and better in general?

I wrote up this client script that grabs a copy of the LocalPlayer character’s current model. Then, using that model it displays a view of the character up in the top corner of the game screen.

Unfortunately, I’ve noticed a little bit of lag when starting and stopping the game which I feel is just a precursor to future issues. How do I make this script less resource-intensive? The last thing I need is for my computer’s fans to have to ramp up to lightspeed just to render a player view.

Any tips are appreciated.

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

--[[
  Destroys any child of a given type residing within the top level of the instance.
  @param {any} instance - The instance that will be traversed.
  @param {string} type - The ClassName of the instances you'de like to destroy.
]]
local function clearChildrenOfType(instance, type)
  local Children = instance:GetChildren();
  for _,c in ipairs(Children) do
    if c:IsA(type) then c:Destroy() end
  end
end

--[[
  Initializes the Viewport Frame
]]
local function initializeViewportFrame()
  
  local ViewportFrame = script.Parent;
  local Heartbeat = RunService.Heartbeat;

  -- Setup the viewports camera
  local Camera = Instance.new("Camera");
  ViewportFrame.CurrentCamera = Camera;
  Camera.Parent = ViewportFrame;

  --[[
    Gets the most up to date copy of the player's characterr model
  ]]
  local function getCurrentCharacterModel() 
    local Character = Players.LocalPlayer.Character;
    if Character == nil then return end   -- Depart if the the Character has not yet loaded.
    
    Character.Archivable = true;  -- Temporarily allow cloning.
    local Frame = Character:Clone();
    Character.Archivable = false;  -- Temporarily allow cloning.
    
    return Frame;
  end

  --[[
    Updates the viewport when the `RenderStepped` event is called.
  ]]
  local function onRenderStepped()
    
    -- Get a pose from the character model
    local Model = getCurrentCharacterModel();
    if Model == nil then return end   -- Depart if the the Character has not yet loaded.

    -- Flush out the last pose in ViewportFrame
    clearChildrenOfType(ViewportFrame, "Model");

    -- Parent the model to the View Port frame
    Model.Parent = ViewportFrame

    -- Update the Camera
    local Root = Model.HumanoidRootPart;
    local InitialCFrame = Root.CFrame * CFrame.Angles(0, math.rad(-12), 0);
    local OffsetCFrame = InitialCFrame:ToWorldSpace(
      CFrame.new(Vector3.new(0, 0.75, -4.5)) * CFrame.Angles(0, math.rad(180), 0)
    );
    Camera.CFrame = OffsetCFrame;
  end

  --[[
    Triggers the `onRenderStepped` function which updates the viewport frame.
  ]]
  Heartbeat:Connect(onRenderStepped);

end

initializeViewportFrame();
1 Like

Overall I don’t see any glaring issues with it. I believe the main issue is the constant cloning of the player model, but I don’t see much of a way around that?

I double checked and referred to Frames | Documentation - Roblox Creator Hub, but I didn’t see you violate any of their “common pitfalls-esque” descriptions.

Also I become overly elated when i see others using some documentation standard, so good job on that front!

Tabs are better than spaces, though ;^)

Edit: Because JSDoc doesn’t syntaxtically help us (lack of Roblox LSP, et cetera), feel free to use Roblox-API-like annotations in your code, such as replacing clearChildrenOfType’s instance parameter from any to Instance.

1 Like

So I’m unsure how you want this to work but as TheEdgyDev said above it’s good practice to try to not Clone() and Destroy() at a frequent pace. If all possible you could make it so the UI never resets hence never reloads and only need to get the object on (X Event) or just on the general load of the client, X Event being potential changes you might want the viewport to change i.e Character’s Appearance which you may or may not desire. But in the future I suggest only using :Clone() and :Destroy() on cases where you have no way around it… Just a general thought, if you can’t get around it I doubt it’ll cause anything greater than a micro change in client usage but still good practice! :slight_smile:

1 Like