Playing Keyframe Sequences

I’ve created a function to play Keyframe sequences without needing to convert them to animation.

PlayKeyframeSequence = function(Model, KeyFrameSequence, SpeedMult)
	local AllKeyFrames = {}

	for i,Keyframe in pairs(KeyFrameSequence:GetKeyframes()) do
		table.insert(AllKeyFrames, {
			Time = Keyframe.Time,
			Keyframe = Keyframe
		})
	end
	table.sort(AllKeyFrames, function(a, b)
		return a.Time < b.Time
	end)
	
	
	local KeyFramePoses = {}
	
	local function GetMotorFromPose(Pose, Keyframe)
		local FullName = Pose:GetFullName()
		local Path = string.split(FullName, ".")
		local KFPosition = table.find(Path, Keyframe.Keyframe.Name)
					
		local Motor6D
		
		for i,v in pairs(Model:GetDescendants()) do

			if v:IsA("Motor6D") and v.Part1.Name == Pose.Name and v.Part0.Name == Pose.Parent.Name then
				Motor6D = v
			end
		end
		return Motor6D
	end
	
	
	for i,Keyframe in pairs(AllKeyFrames) do
		for i,Pose in pairs(Keyframe.Keyframe:GetDescendants()) do
			if Pose:IsA("Pose") then
				if not KeyFramePoses[Pose.Name] then
					KeyFramePoses[Pose.Name] = {
						Motor6D = nil,
						Poses = {},
					}
				end
				local Motor6D = KeyFramePoses[Pose.Name].Motor6D or GetMotorFromPose(Pose, Keyframe)
				if not KeyFramePoses[Pose.Name].Motor6D then
					KeyFramePoses[Pose.Name].Motor6D = Motor6D
				end
				if Motor6D then
					table.insert(KeyFramePoses[Pose.Name].Poses, {Time = Keyframe.Time, Motor6D = Motor6D, Pose = Pose})
				end
			end
		end
	end
	
	
	
	local function GetPositionBetween(T1, T2, Pos)
		
		local TimeIn = Pos - T1
		local MaxTime = T2 - T1
		return TimeIn/MaxTime
		
	end
	
	
	local TimePassed = 0
	local CurrentPoses = {}
	local Running = true
	local EditedMotor6Ds = {}
	
	local Paused = false
	local ForceStopped = false
	local SpeedMult = SpeedMult or 1
	task.spawn(function()
		repeat
			Running = false
			local Poses = {}
			for PoseName, PTB in pairs(KeyFramePoses) do
				local Poses = PTB.Poses
				if #Poses >= 2 then

					local Motor6D = PTB.Motor6D

					local PreviousSPT = Poses[1]
					local NextSPT = Poses[2]

					if NextSPT.Time < TimePassed then
						repeat
							table.remove(Poses, 1)
							PreviousSPT = Poses[1]
							NextSPT = Poses[2]
						until not NextSPT or NextSPT.Time > TimePassed
					end

					if NextSPT then
						local PositionBetween = GetPositionBetween(PreviousSPT.Time, NextSPT.Time, TimePassed)
						local TargetCFrame = PreviousSPT.Pose.CFrame:Lerp(NextSPT.Pose.CFrame, PositionBetween)
						Motor6D.Transform = TargetCFrame
						if not table.find(EditedMotor6Ds, Motor6D) then
							table.insert(EditedMotor6Ds, Motor6D)
						end
						Running = true
					end



				end
			end


			TimePassed += RenderStepped:Wait() * (Paused and 0 or SpeedMult)
		until not Running or ForceStopped
		for i,v in pairs(EditedMotor6Ds) do
			TS:Create(v, TweenInfo.new(0.25, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), {Transform = CF})
		end
	end)

	local function Stop()
		ForceStopped = true
	end
	local function Pause()
		Paused = true
	end
	local function Unpause()
		Paused = false
PS: I'd love to hear feedback on any mistakes or unnecessary things in my code!
9 Likes

Seems to ignore the pose’s EasingDirection, EasingStyle and Weight.

Probably worth pointing out in the post that this is not equivalent to the engine’s own animation player, and point out the differences.

For example, it looks like you do a linear interpolation (Lerp) for the animation.


In the code after the repeat loop, you tween the Transform property of v to CF, which has not been initialised or set to. Is that intended?

2 Likes

“CF” Is set to CFrame.new() I copied it from a large script of mine, so I had it initialized at first.
But you are right about the EasingStyle and Easing Direction

how do you calculate the CF tho🧐

The pose has a property that tells you the CFrame

For EasingDirection and EasingStyle, you could use TweenService:GetValue() instead of CFrame:Lerp()

yes, need replace it into tweenservice becouse updating each renderstep may make it just not load at all.

1 Like

AMAZING!!!, i will use that for my game (Haves a ton of animations… just imagine if does not loads at all…)

I’m testing right now and it appears it doesn’t really support looped animation. Seems like it’s related to PTB.

Could you elaborate on this? I’ve used GetValue but not sure how to implement it correctly? Do I just replace PositionBetween with the returned number?
TS:GetValue(PositionBetween, Enum.EasingStyle[NextSPT.Style], Enum.EasingDirection[NextSPT.Direction])

you guys must play it inside local scripts at this point,network wont handle it at all

i have made module about it KeyframeSequance to Animation v3
Your problem in code were use of tweens they very likely to break timeline and cannot be easily interpeted in sertain point of timeline.
Better late than never…Right???