Just wanted to drop this function, it may not be the best but it does it’s job.
Basically you only need to set Object to the instance you will animate and the Frames has to be a folder that contains the frames for every property in Object
This supports interpolation between frames so clients with more FPS have smoother transitions
So yeah that’s it hope its useful
function CreateFrameAnimation(Object:Instance, Frames:Folder)
-- Data.Start() will start the animation
-- Data.Stop() will stop the animation
-- Data.Destroy() will destroy the object (hopefully avoiding leaks)
local function lerp(start, goal, alpha) return start + (goal - start) * alpha end
local Timepass = 0
local CurrentFrame = 0
local Data = {
Ended = false, -- Indicates if the animation ended
Connection = nil, -- Connection in case you need to forcefully disconnect
Paused = false, -- Toggling this value will pause the animation
FPS = 60, -- On what FPS the animation was made, usually MA2 uses 60 FPS
DestroyOnEnd = false, -- If you want it to automatically destroy itself after the animation ends
}
local ValidProperties:{Folder?} = {}
for i,v in Frames:GetChildren() do
local IsValid = pcall(function() return Object[v.Name] end)
if IsValid and (not Object:FindFirstChild(v.Name)) then
table.insert(ValidProperties, v)
end
end
Data.Start = function()
assert(Data.Connection == nil, "This animation has already started.")
Data.Ended = false
CurrentFrame = 0
Timepass = 0
local frameTime = 1 / Data.FPS
Data.Connection = RunService.Heartbeat:Connect(function(Delta)
if Data.Paused then return end
Timepass += Delta
local alpha = math.clamp(Timepass / frameTime, 0, 1)
local FoundFrame = false
for _, propertyFrames in ValidProperties do
local frame = propertyFrames:FindFirstChild(CurrentFrame)
if frame and frame:IsA("ValueBase") then
FoundFrame = true
local prevFrame = propertyFrames:FindFirstChild(CurrentFrame - 1)
if prevFrame and prevFrame:IsA("ValueBase") then
local val1, val2 = prevFrame.Value, frame.Value
local finalVal = val2
if typeof(val1) == typeof(val2) then
if typeof(val2) == "number" then
finalVal = lerp(val1, val2, alpha)
elseif typeof(val2) == "Vector3" or typeof(val2) == "CFrame" or typeof(val2) == "Color3" then
finalVal = val1:Lerp(val2, alpha)
end
end
Object[propertyFrames.Name] = finalVal
else
Object[propertyFrames.Name] = frame.Value
end
end
end
if not FoundFrame then
Data.Stop()
if Data.DestroyOnEnd then
Data.Destroy()
end
return
end
if Timepass >= frameTime then
Timepass %= frameTime
CurrentFrame += 1
end
end)
end
Data.Stop = function()
assert(Data.Connection ~= nil, "This animation hasn't started.")
Data.Ended = true
CurrentFrame = 0
Timepass = 0
Data.Connection:Disconnect()
Data.Connection = nil
end
Data.Destroy = function()
if Data.Connection then
Data.Stop()
end
table.clear(Data)
end
return Data
end