You’ll want to play the animation on the cloned model, not the original model. A corrected version may look like this:
Clone original model into viewport
v
Hide original model
v
Play the animation on the model clone in the viewport
v
wait(animationTime) then destroy viewport model
v
Show original model again
I would like to have a model that is playing an animation constantly, like /edance for reference. So would this just be the same thing as if I were to loop it with this idea?
Yes, you could either loop the existing animation by repeatedly playing it after it stops, or you can create a looped animation in the animation editor.
Oh so, I can’t play animations on the cloned figure, even though ever single instance that was in the original player’s model is within the cloned model?
Yeah, I recommend cloning a figure specifically for playing the animation. That figure will have its Parent be set as the Viewport. The original figure will not have the animation played, because then you won’t see it on the cloned figure. The cloned figure should have the animation played so you can see it in the viewport.
ViewportFrames do not support animations. What you will need to do specifically is to play the animations on either a proxy model or your actual character model. Then, include a loop in RenderStepped (or bind to it, it is appropriate for this use case) that updates each Motor6D’s Transform property in the ViewportFrame character to your model.
I’ve got you in terms of flow.
Setup:
Model (Actual character or a dummy character)
Dummy characters need to be placed where players can't see them
ViewportFrame Character
Workflow:
Use script below as a template
Play animation on Model
Script:
local RunService = game:GetService("RunService")
local RenderPriority = Enum.RenderPriority.Last.Value
RunService:BindToRenderStep("UpdateVPCharacter", RenderPriority, function()
-- Update Motor6Ds here
end)
RenderStepped allows you to bind a function to be ran before a frame renders. In this instance, you’re rendering a ViewportFrame model with animations, therefore it’s appropriate to put it in RenderStep. That being said, my code sets the function to be hooked with the Last priority, meaning it’s one of the final operations to run before a frame is rendered.
Motor6Ds are the welds that connect characters together and what animations modify in order to make characters or assemblies (models) move the way they do. You can get all of them with an iterative filter through your character:
for _, descendant in pairs(character:GetDescendants()) do
if descendant:IsA("Motor6D") then
print("Motor6D")
end
end
So for the code that you posted, I play the animation before, and then clone the model within the :BindToRenderStep? This isn’t clicking 100%, but I am pretty close.
No. You clone the model into the ViewportFrame then bind a function to RenderStep. If the model is intended to reflect your character’s current movements, then you play the animation as you normally would on the character and it’ll automatically reflect in the ViewportFrame model.
-- Example script
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local LocalPlayer = Players.LocalPlayer
local RenderPriority = Enum.RenderPriority.Last.Value
local character = clonedViewportFrameCharacterGoesHere
RunService:BindToRenderStep("UpdateVPCharacter", RenderPriority, function()
local realCharacter = LocalPlayer.Character -- Do NOT yield
if realCharacter then
for _, descendant in pairs(character:GetDescendants()) do
if descendant:IsA("Motor6D") then
local realMotor = realCharacter:FindFirstChild(descendant.Name, true)
if realMotor then
descendant.Transform = realMotor.Transform
end
end
end
end
end)
I’ll just post my code here, I feel like I am only confusing myself with this.
local plr = game.Players.LocalPlayer
local plrs = game:GetService("Players")
local char = plr.Character or plr.CharacterAdded:wait()
local hum = char:WaitForChild("Humanoid")
local vf = script.Parent
local vfc = Instance.new("Camera", vf)
local idleAnim = nil
local d = Instance.new("Model", workspace)
local anims = script.animations
local part = workspace.Part
local rs = game:GetService('RunService')
local rp = Enum.RenderPriority.Last.Value
vf.CurrentCamera = vfc
d.Name = plr.Name
wait(2)
for k,v in pairs(char:GetChildren()) do
v:Clone().Parent = d
end
d.PrimaryPart = d:FindFirstChild('HumanoidRootPart')
local dHum = d:FindFirstChild('Humanoid')
idleAnim = anims:WaitForChild('idle')
idleAnim = dHum:LoadAnimation(idleAnim)
idleAnim:Play()
d:MoveTo(part.Position)
wait(2)
d.Parent = vf
vfc.CFrame = d.HumanoidRootPart.CFrame*CFrame.new(0,0,-5.5,0,1,0,0)
rs:BindToRenderStep("UpdateVPCharacter", rp, function()
local realCharacter = plr.Character -- Do NOT yield
if realCharacter then
for _, descendant in pairs(char:GetDescendants()) do
if descendant:IsA("Motor6D") then
local realMotor = realCharacter:FindFirstChild(descendant.Name, true)
if realMotor then
descendant.Transform = realMotor.Transform
end
end
end
end
end)
Yeah, there’s a few issues in there that may be driving you towards confusion.
Your code has extremely vague variable names (you should use clear variable names so you’re tracking what you’re doing). This part can help you with readability and tracking what exactly you’re going for. This is more of a side note though and not the main problem.
You aren’t supposed to play the animation on the model you’re parenting to the ViewportFrame; you’re meant to play the animation on the actual character or a proxy model. That means either playing an animation on Player.Character or making a copy of the character twice - one to proxy, one to play animations.
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:wait()
local humanoid = character:WaitForChild("Humanoid")
local viewportFrame = script.Parent
local viewportFrameCamera = Instance.new("Camera")
local viewportFrameCharacter = Instance.new("Model")
local viewportProxyCharacter = Instance.new("Model")
local animations = script.animations
local part = workspace.Part
local idleAnim
viewportFrame.CurrentCamera = viewportFrameCamera
viewportFrameCharacter.Name = LocalPlayer.Name
viewportProxyCharacter.Name = LocalPlayer.Name
wait(2)
for _, object in pairs(char:GetChildren()) do
object:Clone().Parent = viewportFrameCharacter
if object:IsA("BasePart") then
local proxyObject = object:Clone()
proxyObject.Parent = viewportProxyCharacter
proxyObject.Anchored = object.Name == "HumanoidRootPart"
proxyObject.Transparency = 1
end
end
viewportFrameCharacter.PrimaryPart = viewportFrameCharacter.HumanoidRootPart
viewportProxyCharacter.PrimaryPart = viewportProxyCharacter.HumanoidRootPart
local proxyHumanoid = viewportProxyCharacter.Humanoid
idleAnim = proxyHumanoid:LoadAnimation(anims.idle)
idleAnim:Play()
viewportFrameCharacter:MoveTo(part.Position)
viewportFrameCharacter.Parent = viewportFrame
viewportFrameCamera.CFrame = viewportFrameCharacter.HumanoidRootPart.CFrame * CFrame.new(0, 0, -5.5, 0, 1, 0 0)
RunService:BindToRenderStep("UpdateVPCharacter", Enum.RenderPriority.Last.Value, function()
for _, descendant in pairs(viewportFrameCharacter:GetDescendants()) do
if descendant:IsA("Motor6D") then
local realMotor = realCharacter:FindFirstChild(descendant.Name, true)
if realMotor then
descendant.Transform = realMotor.Transform
end
end
end
end)
You’re not the only one. I am also having trouble figuring this out as well. https://gyazo.com/7e5eaf0f05d96f025b93ada1f6a22c4b
[edit: If you can’t view the video, right-click and copy video address and open it in another tab or browser]
I have used similar code to the ones explained previous, just with my own twist but I can guarantee that it should work like the other ones. If needed, I can share what I wrote.
For any of those who are still wondering, try using the newly added WorldModel. Parent one of these inside the ViewportFrame, add any objects you want to animate underneath it, and the animations should play as normal.