Frame animation function built around Moon Animator 2

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
2 Likes

I’m gonna elaborate further because I feel like the post was pretty vague.

When you create a Moon Animation, you will have the “Export Rigs” under the File tab.
Sure, you can rig whatever you were moving so then you can export it as a Roblox Animation.
But when you export the rigs, usually a folder will appear under the object you just animated.

Let’s take a camera for example. You usually animate both the Field of View and the camera CFrame.

When you export the animation with the “Export Rigs” button, you will get a folder under the camera containing 3 folders: “FOV”, “Frames” and “Settings”.

So what you do here is basically rename the name of the folders to the property that is being animated.

So here, the “FOV” folder would be renamed to “FieldOfView” (Camera.FieldOfView) and as for the “Frames”, it would be renamed to “CFrame” (Camera.CFrame).

Now you just have to go and save this somewhere, and when you need to use it, just do the following example:

local Camera = workspace.CurrentCamera
local AnimationData = script.CameraAnimation -- assuming you parented the folder under the script

local Animation = CreateFrameAnimation(Camera, AnimationData) -- creates the object

-- note, you probably already know this but make sure that the camera's CameraType is set to Enum.CameraType.Scriptable
-- otherwise it will conflict with Roblox's camera system and it will most likely flicker

local lastState = Camera.CameraType
Camera.CameraType = Enum.CameraType.Scriptable

Animation.Play() -- plays the animation

while not Animation.Ended do task.wait(1/20) end -- basically waits until the animation ends
-- this is not really reliable so make sure to add sanity checks

Camera.CameraType = lastState -- optionally reset the CameraType to what it was before the animation

Of course you need to adjust this based on how your game’s infrastructure works.

This of course works with other objects such as Parts, Values, anything that you can animate in MA2.

Important; this animator only works with one object at a time.
If you need to animate multiple objects you need to create an animator for each one of them.

I know this might be inconvenient but it’s more focused on animating single objects such as the Camera.

Here’s an example of what you can achieve using this animator*:


*animation might feel delayed due to the fps of the recording

ok thats it bye :3