local TweenService = game:GetService("TweenService")
local Signal = {}
Signal.__index = Signal
-- Constructor
function Signal.new()
return setmetatable({
_bindable = Instance.new("BindableEvent"),
_args = nil,
_argCount = nil,
},Signal)
end
function Signal:Fire(...)
self._args = {...}
self._argCount = select("#", ...)
self._bindable:Fire()
end
function Signal:fire(...)
return self:Fire(...)
end
function Signal:Connect(handler)
if not (type(handler) == "function") then
error(("connect(%s)"):format(typeof(handler)), 2)
end
return self._bindable.Event:Connect(function()
handler(unpack(self._args or {}))
end)
end
function Signal:connect(...)
return self:Connect(...)
end
function Signal:Wait()
self._bindableEvent.Event:Wait()
assert(self._argData, "Missing argument data, likely due to :TweenSize/Position corrupting.")
return unpack(self._args, 1, self._argCount)
end
function Signal:wait()
return self:Wait()
end
function Signal:Destroy()
if self._bindable then
self._bindable:Destroy()
self._bindable = nil
end
self._args = nil
self._argCount = nil
setmetatable(self, nil)
end
function Signal:destroy()
return self:Destroy()
end
local Track = {}
Track.__index = Track
function Track.new(Animator : any, Transform : any, Character : Model, Sequence : KeyframeSequence)
local NewTrack = setmetatable({}, Track)
local TransformAmount = 0
NewTrack.Animator = Animator
NewTrack.KeyframeReached = Signal.new()
NewTrack.OnFinished = Signal.new()
NewTrack.Playing = false
NewTrack.Looped = Sequence.Loop
NewTrack.Smooth = false
NewTrack.FiredEvent = false
NewTrack.Speed = 1
NewTrack.Character = Character
NewTrack.Finished = 0
for _ in Transform do
TransformAmount += 1
end
NewTrack.TransformAmount = TransformAmount
NewTrack.Transform = Transform
local SequenceSorted = {}
for _, Object in Sequence:GetChildren() do
if Object:IsA("Keyframe") then
table.insert(SequenceSorted, Object)
end
end
table.sort(SequenceSorted, function(A, B)
return A.Time < B.Time
end)
NewTrack.Keyframes = SequenceSorted
return NewTrack
end
function Track:AdjustSpeed(Value : number)
self.Speed = Value
end
function Track:Play(speed: number?)
if self.Playing then return end
if speed then
assert(typeof(speed) == "number","Speed parameter must be a number")
end
self.Finished = 0
self.Speed = speed or 1
self.Playing = true
end
function Track:Reset()
for Part1, Motor in self.Transform do
Motor.Index = 1
Motor.Skip = 1
Motor.Done = false
Motor.Alpha = 0
end
end
function Track:Stop()
if not self.Playing then return end
self.Finished = 0
self.Playing = false
self:Reset()
end
function Track:IsFinished()
return self.Finished >= self.TransformAmount
end
function Track:_update(DeltaTime : number)
if self.Playing then
for _,Motor in self.Transform do
if Motor.Object:IsA("Weld") and not Motor.Object.Enabled then
Motor.Object.Enabled = true
end
end
else
for _,Motor in self.Transform do
if Motor.Object:IsA("Weld") and Motor.Object.Enabled then
Motor.Object.Enabled = false
end
end
end
if not self.Playing then return end
local Keyframes = self.Keyframes
self.FiredEvent = false
for Name, Motor in self.Transform do
if Motor.Done then continue end
local CurrentKeyframe = Keyframes[Motor.Index]
local NextKeyframe = Keyframes[Motor.Index + Motor.Skip]
if not NextKeyframe then continue end
local CurrentPose = CurrentKeyframe:FindFirstChild(Name, true)
if not CurrentPose then continue end
local NextPose = NextKeyframe:FindFirstChild(Name, true)
if not NextPose then Motor.Skip += 1 continue end
Motor.Skip = 1
Motor.Alpha += DeltaTime * self.Speed
local Alpha = Motor.Alpha / (NextKeyframe.Time - CurrentKeyframe.Time)
if Alpha >= 1 then
if not self.FiredEvent then
self.KeyframeReached:Fire(NextKeyframe.Name,Motor.Index + Motor.Skip)
self.FiredEvent = true
end
Motor.Alpha = 0
Motor.Index += 1
if Motor.Index >= #Keyframes - 1 then
self.Finished += 1
Motor.Done = true
end
end
local TweenedAlpha = TweenService:GetValue(
math.clamp(Alpha, 0, 1),
Enum.EasingStyle[CurrentPose.EasingStyle.Name],
Enum.EasingDirection[CurrentPose.EasingDirection.Name]
)
if Motor.Object:IsA("Weld") then
Motor.Object.C0 = (Motor.Default * CurrentPose.CFrame):Lerp(Motor.Default * NextPose.CFrame, TweenedAlpha)
else
if self.Smooth then
Motor.Object.CFrame = Motor.Object.CFrame:Lerp((Motor.Default * CurrentPose.CFrame):Lerp(Motor.Default * NextPose.CFrame, TweenedAlpha),10 * DeltaTime)
else
Motor.Object.CFrame = (Motor.Default * CurrentPose.CFrame):Lerp(Motor.Default * NextPose.CFrame, TweenedAlpha)
end
end
end
if self:IsFinished() then
self.OnFinished:Fire()
if self.Looped then
self.Finished = 0
self:Reset()
else
self:Stop()
end
end
end
return Track