
Introduction
As you know, when a player's character spawns, Roblox creates an inside character script called Animate. This script contains information on how characters's animations should work. But roblox's animate script logic and code is kinda bad (really bad).
This module is a multifunctional tool that allows you to simulate Animate script , and also has many settings that allow you to customize the logic of the script
Why should I use BetterAnimate? [❗ DOCUMENTATION ❗]
Info:
- I will no longer update the documentation, since it takes me more time than the development of the update itself. If you want to know what a parameter/method is responsible for, read the hints in the module.
- I would be grateful if someone would make full documentation.
Any Rig Support
Module supports any rig, even without Humanoid, but it must have AnimationController or Humanoid
Build-in Debug
local MyAnimator = BetterAnimate.New(Character):SetDebugEnabled(true)
--or
MyAnimator:SetDebugEnabled(true)
Methods [Functions & Properties]
Documentation may sometimes be incomplete as I may forget to add changes, it’s best to read the documentation inside BetterAnimate ModuleScript.
--Public (I won't describe private values)
local BetterAnimate = require(--[[PATH TO BetterAnimate]])
do type([[ MAIN METHODS ]])
--[[1]] BetterAnimate.GetMoveDirectionName(Vector3.new(0, 1, 0)) --> "UP"
--[[2]] BetterAnimate.GetAnimationData(180426354) --> {ID = 180426354, Instance = Animation, Weigh = FastConfig.DefaultAnimationWeight}
--[[3]] BetterAnimate.GetClassesPreset("R15") --[[> {
Run = {{180426354}},
Walk = {{180426354}},
ETC....
}
]]
--[[4]] BetterAnimate.FixCenterOfMass(PrimaryPart.CurrentPhysicalProperties, PrimaryPart)
--[[5]] BetterAnimate.LocalUtils --> Utils create by BetterAnimate
--[[6]] BetterAnimate.PresetsTag --[[> Tag for GetClassesPreset()
ModuleWithAnimationsPresets:AddTag(BetterAnimate.PresetsTag)
]]
end
do type([[ ANIMATOR METHODS ]])
--TODO Provide more description
local MyAnimator = BetterAnimate.New(Character)
--[[1]] MyAnimator:SetForcedState(Enum.HumanoidStateType.Running.Name)
--[[2]] MyAnimator:SetEventEnabled("NewState") --> Disable/Enable bindable events
--[[3]] MyAnimator:SetDebugEnabled(true) --> Disable/Enable debug (above character)
--[[4]] MyAnimator:SetClassesPreset(ClassesPreset) --> Set pack of classes animations
--[[5]] MyAnimator:SetClassesPreset("Walk", ClassPreset) --> Set class animations
--[[6]] MyAnimator:SetInverseEnabled(true) --> Disable/Enable inverse animation (MoveDirection)
--[[7]] MyAnimator:SetInverseDirection("Down") --> N/A
--[[8]] MyAnimator:SetClassInverse("Run", true) --> N/A
--[[9]] MyAnimator:SetClassTimer("Idle", NumberRange.new(6, 8)) --> N/A
--[[10]] MyAnimator:SetClassMaxTimer("Idle", NumberRange.new(6, 8)) --> N/A
--[[11]] MyAnimator:SetClassEmotable("Idle", true) --> N/A
--[[12]] MyAnimator:SetClassAnimationSpeedAdjust("Run", 16) --> N/A
--[[13]] MyAnimator:SetRunningStateRange(NumberRange.new(0.4, 9)) --> N/A
--[[14]] MyAnimator:SetStateFunction(Enum.HumanoidStateType.Running.Name, function(...) ... end) --> N/A
--[[15]] MyAnimator:AddAnimation("Walk", "Index", AnimationData) --> N/A
--[[16]] MyAnimator:StopClassAnimation() --> Stop current Class animation
--[[17]] MyAnimator:PlayClassAnimation("Wak", 0.1) --> Play Class animation
--[[18]] MyAnimator:PlayToolAnimation(2959295295?) --> Play Tool animation
--[[19]] MyAnimator:StopToolAnimation() --> Stop Tool animation
--[[20]] MyAnimator:StopEmote() --> Stop Emote animation
--[[21]] MyAnimator:PlayEmote(5252525) --> Play Emote animation
--[[22]] MyAnimator:Step(task.wait(0.03), Humanoid:GetState().Name) --> Force update
--[[23]] MyAnimator:Destroy() --> Destroy animator
end
Play Custom Tool Animation
local MyAnimator = BetterAnimate.New(Character)
MyAnimator:PlayToolAnimation(507768375) -- 507768375 for r15
Fix Center Of Mass
When equiping tool while emoting your сenter of character mass changes and let to move really fast while jumping (example).
BetterAnimate have function to fix this:
local PrimaryPart = Character.PrimaryPart
local CurrentPhysicalProperties = PrimaryPart.CurrentPhysicalProperties
local BetterAnimate = require(ModuleScript_BetterAnimate)
local FixCenterOfMass = BetterAnimate.FixCenterOfMass
FixCenterOfMass(CurrentPhysicalProperties, PrimaryPart)
Thanks to @treebee63
Optimization
You can control how often the update will occur.
local BetterAnimate = require(ModuleScript_BetterAnimate)
local MyAnimator = BetterAnimate.New(Character)
local Rate = 0.03
MyAnimator.Trove:Add(task.defer(function()
while true do
MyAnimator:Step(task.wait(Rate), Character.Humanoid:GetState())
end
end)
Inverse Animations

To enable this you must call :SetInverseEnabled(true) method
local BetterAnimate = require(ModuleScript_BetterAnimate)
local MyAnimator = BetterAnimate.New(Character):SetInverseEnabled(true)
Also if you want to change, when Inverse should work, use methods:
MyAnimator:SetInverseDirection("Backward", true) --> :GetInverse() will return -1
MyAnimator:SetClassInverse("Run", true) --> Class now support inverse
Update Animations In Real Time
local RunPreset = {
Run1 = 507767714-- for R15
}
local WalkPreset = {
Walk1 = 507777826 -- for R15
}
local BetterAnimate = require(ModuleScript_BetterAnimate)
local MyAnimator = BetterAnimate.New(Character)
MyAnimator:SetClassesPreset({
Run = RunPreset,
Walk = WalkPreset
})
--OR
MyAnimator:SetClassPreset("Walk", WalkPreset)
--OR
MyAnimator:AddAnimation("Walk", "Walk1", 507777826 --[[or nil if you want to remove]])
Events [Signals]
BetterAnimate have 4 Events:
local BetterAnimate = require(ModuleScript_BetterAnimate)
local MyAnimator = BetterAnimate.New(Character)
local Events = MyAnimator.Events
-- Fires on new state (Running, Falling, etc...)
Events.NewState:Connect(function(State: string) end)
-- Fires on new animation (Except tool animation)
Events.NewAnimation:Connect(function(AnimationClass: string, AnimationName: string, AnimationTable) end)
-- Fires on new Vector3 MoveDirection
Events.NewMoveDirection:Connect(function(MoveDirection: Vector3, MoveDirectionName: string) end)
-- Fires on animation keyframe reached (alternative to GetMarkerReachedSignal) (Including tool animation)
Events.KeyframeReached:Connect(function(Keyframe: string) end)
Chaining
BetterAnimate supports “Set” chain methods:
local BetterAnimate = require(ModuleScript_BetterAnimate)
local MyAnimator = BetterAnimate.New(Character)
:SetClassEmotable("Idle", true)
:SetRunningStateRange(Vector2.new(4, 9))
:SetInverseEnabled(true)
Play Emote
local BetterAnimate = require(ModuleScript_BetterAnimate)
local MyAnimator = BetterAnimate.New(Character)
local DirectionEvent = MyAnimator.Events["NewMoveDirection"]
DirectionEvent:Connect(function(MoveDirection: Vector3, MoveDirectionName: string)
if MoveDirection.Magnitude > 1 --[[you can change to 0 if you want]] then
MyAnimator:StopEmote()
end
end)
MyAnimator:PlayEmote(MyAnimator._RigType == `R6` and 182436842 or 15609995579)
To customize when Character can emote, use :SetClassEmotable(“Idle”, true)
MyAnimator:SetClassEmotable("Idle", true)
FastConfig
List of values, that can be changed at any time manually
* R6ClimbFix --> Fix r6 climbing speed
* EmoteIngnoreEmotable --> [DEPRECATED]
* AnimationSpeedMultiplier --> `AnimationTrack:AdjustSpeed(Speed * FastConfig.AnimationSpeedMultiplier)`
* AnimationPlayTransition --> `AnimationTrack:Play(FastConfig.AnimationPlayTransition)`
* AnimationStopTransition --> `AnimationTrack:Stop(FastConfig.AnimationStopTransition)`
* ToolAnimationPlayTransition --> `AnimationTrack:Play(FastConfig.ToolAnimationPlayTransition)`
* ToolAnimationStopTransition --> `AnimationTrack:Stop(FastConfig.ToolAnimationStopTransition)`
* WaitFallOnJump --> Wait until play Fall animation on jump
* DefaultAnimationLength --> If Animation.Length == 0 it will use `FastConfig.DefaultAnimationLength`
* DefaultAnimationWeight --> Chance of animation
* AnimationPriority --> `AnimationTrack.Priority = FastConfig.AnimationPriority`
* ToolAnimationPriority --> `AnimationTrack.Priority = FastConfig.ToolAnimationPriority`
* MoveDirection --> Set your own MoveDirection (Don't forget to update it every `:Step()`)
* SetAnimationOnIdDifference --> New animation (Instance) will play if `Animation.AnimationId` is not not equal to the old one
Example:
local BetterAnimate = require(ModuleScript_BetterAnimate)
local MyAnimator = BetterAnimate.New(Character)
local FastConfig = MyAnimator.FastConfig
FastConfig.R6ClimbFix = true
FastConfig.AnimationSpeedMultiplier = 2
FastConfig.DefaultAnimationWeight = 15
How to setup
local Service_Players = game:GetService(`Players`)
local Service_ReplicatedStorage = game:GetService(`ReplicatedStorage`)
local ModuleScript_BetterAnimate = Service_ReplicatedStorage:WaitForChild(`BetterAnimate`)
local BetterAnimate = require(ModuleScript_BetterAnimate)
local LocalPlayer = Service_Players.LocalPlayer
local Character = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait()
local Humanoid = Character:FindFirstChildWhichIsA(`Humanoid`) :: Humanoid
local PrimaryPart = Character.PrimaryPart or Character:GetPropertyChangedSignal(`PrimaryPart`):Wait()-- and Character.PrimaryPart
PrimaryPart = Character.PrimaryPart
local _PhysicalProperties = PrimaryPart.CurrentPhysicalProperties
local ClassesPreset = BetterAnimate.GetClassesPreset(Humanoid.RigType.Name)
local MyAnimator = BetterAnimate.New(Character)
:SetInverseEnabled(true)
:SetClassesPreset(ClassesPreset)
:SetDebugEnabled(true)
MyAnimator.FastConfig.R6ClimbFix = true
do type([[ Events (Signals) ]])
MyAnimator.Events.MarkerReached:Connect(function(Keyframe: string)
--print(`Keyframe reached {Keyframe}`)
end)
MyAnimator.Events.NewMoveDirection:Connect(function(MoveDirection: Vector3, MoveDirectionName: BetterAnimate.BetterAnimate_Directions)
if MoveDirection.Magnitude > 0 then
MyAnimator:StopEmote()
end
end)
MyAnimator.Events.NewAnimation:Connect(function(Class: string, Index: any, AnimationData: BetterAnimate.BetterAnimate_AnimationData)
--print(`New animation {Index}`)
end)
MyAnimator.Events.NewState:Connect(function(State: string)
--print(`New state {State}`)
end)
end
do type([[ Tool ]])
MyAnimator.Trove:Add(Character.ChildAdded:Connect(function(Descendant)
if Descendant:IsA(`Tool`) then
MyAnimator:PlayToolAnimation()
end
BetterAnimate.FixCenterOfMass(_PhysicalProperties, PrimaryPart)
end))
MyAnimator.Trove:Add(Character.ChildRemoved:Connect(function(Descendant)
if Descendant:IsA(`Tool`) then
MyAnimator:StopToolAnimation()
end
BetterAnimate.FixCenterOfMass(_PhysicalProperties, PrimaryPart)
end))
end
do type([[ Personal tests ]]) -- Some test
do
MyAnimator.Trove:Add(script:GetAttributeChangedSignal(`Debug`):Connect(function()
MyAnimator:SetDebugEnabled(script:GetAttribute(`Debug`))
end))
end
do
MyAnimator.Trove:Add(script:GetAttributeChangedSignal(`Emote`):Connect(function()
if script:GetAttribute(`Emote`) then
MyAnimator:PlayEmote(MyAnimator._RigType == `R6` and 182436842 or 15609995579)
else
MyAnimator:StopEmote()
end
end))
end
do type([[ CreateAnimationManager example ]])
local AnimationManager
MyAnimator.Trove:Add(script:GetAttributeChangedSignal(`CreateAnimationManager`):Connect(function()
if script:GetAttribute(`CreateAnimationManager`) then
AnimationManager = MyAnimator:CreateAnimationManager(10714338461)
AnimationManager:SetPrority(Enum.AnimationPriority.Action4)
AnimationManager:SetOnDidLoop(function()
print(`Looped`)
end)
AnimationManager:SetOnEnded(function()
print(`Ended`)
end)
AnimationManager:Play()
else
if AnimationManager then
AnimationManager:Destroy()
end
end
end))
end
end
do type([[ Animations logic ]])
Humanoid.Died:Once(function()
MyAnimator:Destroy()
end)
local NextState = nil
MyAnimator.Trove:Add(Humanoid.Jumping:Connect(function() -- Deffering, since we don't use Humanoid.StateChanged
MyAnimator:SetForcedState(Enum.HumanoidStateType.Jumping.Name)
end))
MyAnimator.Trove:Add(task.defer(function()
while true do
MyAnimator.FastConfig.MoveDirection = PrimaryPart.CFrame:VectorToObjectSpace(Humanoid.MoveDirection)
MyAnimator:Step(task.wait(), Humanoid:GetState().Name)
end
end))
end
do type([[ Roblox buildin PlayEmote ]])
local PlayEmote = script:FindFirstChild(`PlayEmote`)
if PlayEmote and PlayEmote:IsA(`BindableFunction`) then
PlayEmote.OnInvoke = function(Animation: string | Animation) --[You must to return true]]
if typeof(Animation) == `Instance` then
--print(Animation.AnimationId)
return true, MyAnimator:PlayEmote(Animation)
else -- string
--Im bored, write your own method on how to get these animations
end
end
end
end
| Icon | Link | Note |
|---|---|---|
| Module | Get latest version | |
| Playground | Test BetterAnimate | |
| Github | Open source code |
Feel free to give feedback ![]()

