So I was bored and basically wrote a wrapper for handling animations I guess.
--! strict
export type Construct = {
new : (Character : Model) -> (),
Play : (Construct, Animation : number | Animation, Priority : Enum.AnimationPriority, Looped : boolean?, FadeIn : number?, Weight : number?, Speed : number?, FadeOut : number?) -> (),
Stop : (Construct, FadeOut : number?) -> (),
StopAtKeyFrame : (Construct, KeyFrame : string, FadeOut : number?) -> (),
GetAnimation : (Construct) -> Animation | nil,
SetPriority : (Construct, Priority : Enum.AnimationPriority) -> (),
SetSpeed : (Construct, Speed : number) -> (),
SetWeight : (Construct, Weight : number, FadeTime : number?) -> (),
GetKeyFrameTime : (Construct, KeyFrame : string) -> number?,
MarkerReachedSignal : (Construct, Marker : string, callBack : () -> ()) -> () -> (),
ClearMarkers : (Construct, Marker : string?) -> (),
OnLoop : (Construct, callBack : () -> ()) -> () -> (),
OnEnded : (Construct, callBack : () -> ()) -> (),
}
export type AnimationController = {
_animation : Animation,
_markers : {
[string] : {
[number] : () -> ()
}
},
_markerConnections : {
[string] : RBXScriptConnection
},
Character : Model,
Animator : Animator,
_loopConnection : RBXScriptConnection,
_loopCallbacks : {
[number] : () -> (),
},
animationTrack : AnimationTrack,
}
--> Services
local Players = game:GetService('Players')
--> Player
local Player = Players.LocalPlayer
--> Class
local AnimationController = {}
AnimationController.__index = AnimationController
--> Constructor
function AnimationController.new(Character : Model) : Construct & AnimationController
local self = setmetatable({}, AnimationController)
self.Character = Character
self.Animator = nil
if Character:FindFirstChildWhichIsA('Humanoid') ~= nil then
local Humanoid = Character:FindFirstChildWhichIsA('Humanoid')
self.Animator = Humanoid:FindFirstChildWhichIsA('Animator')
else
if Character:FindFirstChildWhichIsA('AnimationController') then
local _AnimationController = Character:FindFirstChildWhichIsA('AnimationController')
self.Animator = _AnimationController:FindFirstChildWhichIsA('Animator')
end
end
self._animation = nil
self.animationTrack = nil
self._markers = {}
self._markerConnections = {}
self._loopConnection = nil
self._loopCallbacks = {}
return self :: Construct & AnimationController
end
--> Methods
function AnimationController:Play(Animation : number | Animation, Priority : Enum.AnimationPriority, Looped : boolean?, FadeIn : number?, Weight : number?, Speed : number?, FadeOut : number?)
self = self :: Construct & AnimationController
if self.Animator == nil then return warn(`No animator object was found in {self.Character.Name}`) end
if Looped == nil then Looped = false end
if FadeIn == nil then FadeIn = 0 end
if Weight == nil then Weight = 1 end
if Speed == nil then Speed = 1 end
if FadeOut == nil then FadeOut = 0 end
if self._animation ~= nil then
self._animation:Destroy()
end
self._animation = if typeof(Animation) ~= 'number' and Animation:IsA('Animation') then Animation else Instance.new('Animation')
if self.animationTrack ~= nil then
self:Stop(FadeOut)
end
if self._animation.AnimationId == '' then
self._animation.AnimationId = `rbxassetid://{Animation}`
end
self.animationTrack = self.Animator:LoadAnimation(self._animation)
self.animationTrack.Priority = Priority
self.animationTrack:Play(FadeIn, Weight, Speed)
self.animationTrack.Looped = Looped
if not Looped then
self.animationTrack.Ended:Wait()
self.animationTrack:Destroy()
self.animationTrack = nil
end
end
function AnimationController:Stop(FadeOut : number?)
self = self :: Construct & AnimationController
if FadeOut == nil then FadeOut = 0 end
if self.animationTrack == nil then return end
self.animationTrack:Stop(FadeOut)
self.animationTrack.Ended:Wait()
self.animationTrack:Destroy()
self.animationTrack = nil
end
function AnimationController:OnLoop(callBack : () -> ()) : nil | () -> ()
self = self :: Construct & AnimationController
if self.animationTrack == nil then return end
if self.animationTrack.Looped == false then return end
table.insert(self._loopCallbacks, callBack)
if self._loopConnection == nil then
self._loopConnection = self.animationTrack.DidLoop:Connect(function()
for _, v in pairs(self._loopCallbacks) do
v()
end
end)
end
return function()
local index = table.find(self._loopCallbacks, callBack)
if index ~= nil then
table.remove(self._loopCallbacks, index)
end
end
end
function AnimationController:OnEnded(callBack : () -> ())
self = self :: Construct & AnimationController
if self.animationTrack == nil then return end
if self.animationTrack.IsPlaying == true then
self.animationTrack.Ended:Once(callBack)
else
callBack()
end
end
function AnimationController:StopAtKeyFrame(KeyFrame : string, FadeOut : number?)
self = self :: Construct & AnimationController
if self.animationTrack == nil then return end
if KeyFrame == nil then return end
self.animationTrack.KeyframeReached:Once(function(name : string)
if name == KeyFrame then
self:Stop(FadeOut or 0)
end
end)
end
function AnimationController:SetPriority(Priority : Enum.AnimationPriority)
self = self :: Construct & AnimationController
if self.animationTrack == nil then return end
self.animationTrack.Priority = Priority
end
function AnimationController:SetSpeed(Speed : number)
self = self :: Construct & AnimationController
if self.animationTrack == nil then return end
self.animationTrack:AdjustSpeed(Speed)
end
function AnimationController:SetWeight(Weight : number, FadeTime : number?)
self = self :: Construct & AnimationController
if self.animationTrack == nil then return end
if FadeTime == nil then FadeTime = 0 end
self.animationTrack:AdjustWeight(Weight, FadeTime)
end
function AnimationController:MarkerReachedSignal(Marker : string, callBack : () -> ()) : nil | () -> ()
self = self :: Construct & AnimationController
if self.animationTrack == nil then return end
if self._markers[Marker] == nil then
self._markers[Marker] = {}
end
table.insert(self._markers[Marker], callBack)
if self._markerConnections[Marker] == nil then
self._markerConnections[Marker] = self.animationTrack:GetMarkerReachedSignal(Marker):Connect(function()
for _, v in pairs(self._markers[Marker]) do
v()
end
end)
end
return function()
local index = table.find(self._markers[Marker], callBack)
if index ~= nil then
table.remove(self._markers[Marker], index)
end
end
end
function AnimationController:ClearMarkers(Marker : string?) --> Don't pass a argument to clear all markers
self = self :: Construct & AnimationController
if Marker == nil then
self._markers = {}
for _, v in pairs(self._markerConnections) do
v:Disconnect()
end
elseif Marker ~= nil then
local MarkerTable = self._markers[Marker]
if MarkerTable ~= nil then
table.clear(MarkerTable)
self._markers[Marker] = nil
end
if self._markerConnections[Marker] ~= nil then
self._markerConnections[Marker]:Disconnect()
end
end
end
function AnimationController:GetKeyFrameTime(KeyFrame : string) : number?
self = self :: Construct & AnimationController
if self.animationTrack == nil then return end
return self.animationTrack:GetTimeOfKeyframe(KeyFrame)
end
function AnimationController:GetAnimation() : Animation | nil
self = self :: Construct & AnimationController
if self.animationTrack == nil then return nil end
return self.animationTrack.Animation
end
--> return
return AnimationController
Documentation:
Creating a new animation controller
Start by setting up a new class for the animation controller
local Character = game.Players.LocalPlayer.Character --> Can be a model of a NPC or anything like that
local Controller = require(path-to-module)
local AnimationController = Controller.new(Character)
Playing an animation
Play a simple animation either from an animation id or a animation instance
AnimationController:Play(1234567, Enum.AnimationPriority.Idle, true, 0, 1, 1, 0)
-- OR
local Animation = Instance.new('Animation')
Animation.AnimationId = 'rbxassetid://1234567'
AnimationController:Play(Animation, Enum.AnimationPriority.Idle, true, 0, 1, 1, 0)
Stopping an animation
Stop the current animation (this will fire all callbacks used on OnEnded)
AnimationController:Stop(0)
Stopping an animation at a key frame
Stop the current animation once it hits a certain key frame
AnimationController:StopAtKeyFrame('TestKeyFrame', 0)
Getting the animation instance
Get the current animation tracks animation instance
local Animation = AnimationController:GetAnimation()
Setting the priority
Set or change the priority of the current animation
AnimationController:SetPriority(Enum.AnimationPriority.Action)
Setting the speed
Set or change the speed of the current animation
AnimationController:SetSpeed(5)
Setting the weight
Set or change the weight of the current animation
AnimationController:SetWeight(0.5, 0)
Get a Key Frames time
Get the time a key frame is located
local TimeOfKeyFrame = AnimationController:GetKeyFrameTime('TestKeyFrame')
Detecting when a marker is reached
Set callbacks for when certain markers are reached on the animation. (returns a function to remove the callback)
local callBack_1 = AnimationController:MarkerReachedSignal('MarkerName', function()
print('Marker reached')
end)
-- Want to remove callBack_1 from the MarkerReached signal? call callBack_1 as a function!
callBack_1()
Clearing markers
Clear all markers or certain markers from the marker reached signal
-- Clearing ALL markers
AnimationController:ClearMarkers()
-- Clearing ONE marker
AnimationController:ClearMarkers('MarkerName')
Detecting when the animation did a loop
Add callbacks to fire when the animation has done a loop. (returns a function to remove the callback)
local onLoop_1 = AnimationController:OnLoop(function()
print('Did loop 1')
end)
-- Want to remove onLoop_1 from the DidLoop signal? call it as a function!
onLoop_1()
Detect when the animation has fully ended
Add a callback to when the animation fully ends
AnimationController:OnEnded(function()
print('Animation Ended')
end)
Any feedback / criticism or suggestions is appreciated!
Market place link: AnimationController - Creator Store (roblox.com)