I was already trying to find a solution to this on the forum, with only some topics having the same issue as me, but no fix was found.
This is going to be a pretty long post, so bear with me here.
I have a forked Animate LocalScript in my game (R6), and it seems to be completely filling up the 256 AnimationTrack limit for Animators:
I started seeing the warning in the error report tab a while ago, so I did some research. Turns out this warning only appears if you exceed 256 PLAYING animations, not LOADED animations. I made a simple script to catch that warning and print all playing animations:
local anim: Animator = script.Parent.Parent:WaitForChild("Humanoid"):WaitForChild("Animator")
function check()
if #anim:GetPlayingAnimationTracks() > 256 then
local tableString = ""
for _, val: AnimationTrack in anim:GetPlayingAnimationTracks() do
tableString ..= val.Animation.AnimationId..",\n"
end
warn("thats quite a lot of animations\n", tableString)
end
end
anim.AnimationPlayed:Connect(check)
I was met with this in the error report tab:
(The list goes on for a while)
The interesting thing here is that all of those are 1 animation, specifically this one, uploaded by Roblox:
There were more warnings, with more unique animations:
Some of these are made by me, but the important thing is: they are ALL played from the Animate script.
So I went out looking for any faults in the script, and ultimately found nothing. While I do use a forked version, from what I can tell, most of the functions are identical to the most up to date version.
My version completely removes the tool animations, and adds some extra functionality to the KeyframeReached function, as well as playing an extra Sprint animation. My game does not parent the player character to nil, or anywhere other than workspace for that matter.
Full script:
local Figure = script.Parent
local Torso = Figure:WaitForChild("Torso")
local RightShoulder = Torso:WaitForChild("Right Shoulder")
local LeftShoulder = Torso:WaitForChild("Left Shoulder")
local RightHip = Torso:WaitForChild("Right Hip")
local LeftHip = Torso:WaitForChild("Left Hip")
local Neck = Torso:WaitForChild("Neck")
local Humanoid = Figure:WaitForChild("Humanoid")
local pose = "Standing"
local plr = game.Players.LocalPlayer
local animUtil = require(game.ReplicatedStorage.UtilityModules.AnimationUtil) -- contents listed below
local dataUtil = require(game.ReplicatedStorage.UtilityModules.DataUtil) -- explanation also below
local localData = dataUtil:GetLocalData()
local currentAnim = ""
local currentAnimInstance = nil
local currentAnimTrack = nil
local currentAnimKeyframeHandler = nil
local currentAnimSpeed = 1.0
local animTable = {}
local animNames = {
idle = {
{ id = "http://www.roblox.com/asset/?id=180435571", weight = 9 },
{ id = "http://www.roblox.com/asset/?id=180435792", weight = 1 }
},
walk = {
{ id = "rbxassetid://6650992691", weight = 10 }
},
run = {
{ id = "rbxassetid://6650992691", weight = 10 }
},
sprint = {
{ id = "rbxassetid://6651141020", weight = 10 }
},
jump = {
{ id = "http://www.roblox.com/asset/?id=125750702", weight = 10 }
},
fall = {
{ id = "http://www.roblox.com/asset/?id=180436148", weight = 10 }
},
climb = {
{ id = "http://www.roblox.com/asset/?id=180436334", weight = 10 }
},
sit = {
{ id = "http://www.roblox.com/asset/?id=178130996", weight = 10 }
},
wave = {
{ id = "http://www.roblox.com/asset/?id=128777973", weight = 10 }
},
point = {
{ id = "http://www.roblox.com/asset/?id=128853357", weight = 10 }
},
dance1 = {
{ id = "http://www.roblox.com/asset/?id=182435998", weight = 10 },
},
dance2 = {
{ id = "http://www.roblox.com/asset/?id=182436842", weight = 10 },
},
dance3 = {
{ id = "http://www.roblox.com/asset/?id=182436935", weight = 10 },
},
laugh = {
{ id = "http://www.roblox.com/asset/?id=129423131", weight = 10 }
},
cheer = {
{ id = "http://www.roblox.com/asset/?id=129423030", weight = 10 }
},
}
local dances = {"dance1", "dance2", "dance3"}
-- Existance in this list signifies that it is an emote, the value indicates if it is a looping emote
local emoteNames = { wave = false, point = false, dance1 = true, dance2 = true, dance3 = true, laugh = false, cheer = false}
function configureAnimationSet(name, fileList)
if (animTable[name] ~= nil) then
for _, connection in pairs(animTable[name].connections) do
connection:disconnect()
end
end
animTable[name] = {}
animTable[name].count = 0
animTable[name].totalWeight = 0
animTable[name].connections = {}
-- check for config values
local config = script:FindFirstChild(name)
if (config ~= nil) then
table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
local idx = 1
for _, childPart in pairs(config:GetChildren()) do
if (childPart:IsA("Animation")) then
table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
animTable[name][idx] = {}
animTable[name][idx].anim = childPart
local weightObject = childPart:FindFirstChild("Weight")
if (weightObject == nil) then
animTable[name][idx].weight = 1
else
animTable[name][idx].weight = weightObject.Value
end
animTable[name].count = animTable[name].count + 1
animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
idx = idx + 1
end
end
end
-- fallback to defaults
if (animTable[name].count <= 0) then
for idx, anim in pairs(fileList) do
animTable[name][idx] = {}
animTable[name][idx].anim = Instance.new("Animation")
animTable[name][idx].anim.Name = name
animTable[name][idx].anim.AnimationId = anim.id
animTable[name][idx].weight = anim.weight
animTable[name].count = animTable[name].count + 1
animTable[name].totalWeight = animTable[name].totalWeight + anim.weight
end
end
end
-- Setup animation objects
function scriptChildModified(child)
local fileList = animNames[child.Name]
if (fileList ~= nil) then
configureAnimationSet(child.Name, fileList)
end
end
script.ChildAdded:connect(scriptChildModified)
script.ChildRemoved:connect(scriptChildModified)
-- Clear any existing animation tracks
-- Fixes issue with characters that are moved in and out of the Workspace accumulating tracks
local animator = if Humanoid then Humanoid:FindFirstChildOfClass("Animator") else nil
if animator then
local animTracks = animator:GetPlayingAnimationTracks()
for i, track in animTracks do
track:Stop(0)
track:Destroy()
end
end
for name, fileList in pairs(animNames) do
configureAnimationSet(name, fileList)
end
-- ANIMATION
-- declarations
local jumpAnimTime = 0
local jumpAnimDuration = 0.3
local fallTransitionTime = 0.3
local jumpMaxLimbVelocity = 0.75
-- functions
function stopAllAnimations()
local oldAnim = currentAnim
-- return to idle if finishing an emote
if (emoteNames[oldAnim] ~= nil and emoteNames[oldAnim] == false) then
oldAnim = "idle"
end
currentAnim = ""
currentAnimInstance = nil
if (currentAnimKeyframeHandler ~= nil) then
currentAnimKeyframeHandler:disconnect()
end
if (currentAnimTrack ~= nil) then
currentAnimTrack:Stop()
currentAnimTrack:Destroy()
currentAnimTrack = nil
end
return oldAnim
end
function setAnimationSpeed(speed)
if speed ~= currentAnimSpeed then
currentAnimSpeed = speed
currentAnimTrack:AdjustSpeed(currentAnimSpeed)
end
end
function keyFrameReachedFunc(frameName)
if (frameName == "End") then
local repeatAnim = currentAnim
-- return to idle if finishing an emote
if (emoteNames[repeatAnim] ~= nil and emoteNames[repeatAnim] == false) then
repeatAnim = "idle"
end
local animSpeed = currentAnimSpeed
playAnimation(repeatAnim, 0.0, Humanoid)
setAnimationSpeed(animSpeed)
elseif (frameName == "WalkSound" and localData.Running.Value == false) or (frameName == "RunSound" and localData.Running.Value == true) then
script.Parent.Scripts.FootstepHandler.LocalFootsteps:SetAttribute("Do", true)
end
end
-- Preload animations
function playAnimation(animName, transitionTime, humanoid)
local roll = math.random(1, animTable[animName].totalWeight)
local origRoll = roll
local idx = 1
while (roll > animTable[animName][idx].weight) do
roll = roll - animTable[animName][idx].weight
idx = idx + 1
end
local anim = animTable[animName][idx].anim
-- switch animation
if (anim ~= currentAnimInstance) then
if (currentAnimTrack ~= nil) then
currentAnimTrack:Stop(transitionTime)
currentAnimTrack:Destroy()
end
currentAnimSpeed = 1.0
-- load it to the humanoid; get AnimationTrack
currentAnimTrack = animUtil:LoadAnimation(anim)
currentAnimTrack.Priority = Enum.AnimationPriority.Core
-- play the animation
currentAnimTrack:Play(transitionTime)
currentAnim = animName
currentAnimInstance = anim
-- set up keyframe name triggers
if (currentAnimKeyframeHandler ~= nil) then
currentAnimKeyframeHandler:disconnect()
end
currentAnimKeyframeHandler = currentAnimTrack.KeyframeReached:connect(keyFrameReachedFunc)
end
end
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
function onRunning(speed)
if speed > 0.01 then
if localData.Running.Value == false then
playAnimation("walk", 0.1, Humanoid)
if currentAnimInstance and (currentAnimInstance.Name == "WalkAnim") then
setAnimationSpeed(speed / 14.5)
end
else
playAnimation("sprint", 0.1, Humanoid)
if currentAnimInstance and (currentAnimInstance.Name == "SprintAnim") then
setAnimationSpeed(speed / 14.5)
end
end
pose = "Running"
else
if emoteNames[currentAnim] == nil then
playAnimation("idle", 0.1, Humanoid)
pose = "Standing"
end
end
end
function onDied()
pose = "Dead"
end
function onJumping()
playAnimation("jump", 0.1, Humanoid)
jumpAnimTime = jumpAnimDuration
pose = "Jumping"
end
function onClimbing(speed)
playAnimation("climb", 0.1, Humanoid)
setAnimationSpeed(speed / 12.0)
pose = "Climbing"
end
function onGettingUp()
pose = "GettingUp"
end
function onFreeFall()
if (jumpAnimTime <= 0) then
playAnimation("fall", fallTransitionTime, Humanoid)
end
pose = "FreeFall"
end
function onFallingDown()
pose = "FallingDown"
end
function onSeated()
pose = "Seated"
end
function onPlatformStanding()
pose = "PlatformStanding"
end
function onSwimming(speed)
if speed > 0 then
pose = "Running"
else
pose = "Standing"
end
end
function moveSit()
RightShoulder.MaxVelocity = 0.15
LeftShoulder.MaxVelocity = 0.15
RightShoulder:SetDesiredAngle(3.14 /2)
LeftShoulder:SetDesiredAngle(-3.14 /2)
RightHip:SetDesiredAngle(3.14 /2)
LeftHip:SetDesiredAngle(-3.14 /2)
end
local lastTick = 0
function move(time)
local amplitude = 1
local frequency = 1
local deltaTime = time - lastTick
lastTick = time
local climbFudge = 0
local setAngles = false
if (jumpAnimTime > 0) then
jumpAnimTime = jumpAnimTime - deltaTime
end
if (pose == "FreeFall" and jumpAnimTime <= 0) then
playAnimation("fall", fallTransitionTime, Humanoid)
elseif (pose == "Seated") then
playAnimation("sit", 0.5, Humanoid)
return
elseif (pose == "Running") then
if localData.Running.Value == false then
playAnimation("walk", 0.1, Humanoid)
else
playAnimation("sprint", 0.1, Humanoid)
end
elseif (pose == "Dead" or pose == "GettingUp" or pose == "FallingDown" or pose == "Seated" or pose == "PlatformStanding") then
stopAllAnimations()
amplitude = 0.1
frequency = 1
setAngles = true
end
if (setAngles) then
local desiredAngle = amplitude * math.sin(time * frequency)
RightShoulder:SetDesiredAngle(desiredAngle + climbFudge)
LeftShoulder:SetDesiredAngle(desiredAngle - climbFudge)
RightHip:SetDesiredAngle(-desiredAngle)
LeftHip:SetDesiredAngle(-desiredAngle)
end
end
-- connect events
Humanoid.Died:connect(onDied)
Humanoid.Running:connect(onRunning)
Humanoid.Jumping:connect(onJumping)
Humanoid.Climbing:connect(onClimbing)
Humanoid.GettingUp:connect(onGettingUp)
Humanoid.FreeFalling:connect(onFreeFall)
Humanoid.FallingDown:connect(onFallingDown)
Humanoid.Seated:connect(onSeated)
Humanoid.PlatformStanding:connect(onPlatformStanding)
Humanoid.Swimming:connect(onSwimming)
-- setup emote chat hook
plr.Chatted:connect(function(msg)
local emote = ""
if msg == "/e dance" then
emote = dances[math.random(1, #dances)]
elseif (string.sub(msg, 1, 3) == "/e ") then
emote = string.sub(msg, 4)
elseif (string.sub(msg, 1, 7) == "/emote ") then
emote = string.sub(msg, 8)
end
if (pose == "Standing" and emoteNames[emote] ~= nil) then
playAnimation(emote, 0.1, Humanoid)
end
end)
-- initialize to idle
playAnimation("idle", 0.1, Humanoid)
pose = "Standing"
while Figure.Parent ~= nil do
local _, time = wait(0.1)
move(time)
end
The key differences are:
- Some extra declarations on the top, will explain below
- Tool animations completely removed
- Extra “sprint” animation in
animNames
- function
keyFrameReachedFunc
on line 229 includes extra if statement for footstep sounds - function
onRunning
on line 283 includes extra if statement for playing the sprint animation - function
move
on line 379 includes extra if statement also for playing the sprint animation
External modules:
AnimationUtil:
local Animations = {}
function Animations.GetAnim(animation)
if typeof(animation) == "Instance" and animation:IsA("Animation") then
return animation
elseif typeof(animation) == "string" then
local anim = Instance.new("Animation")
anim.Name = "TempAnimation"
anim.AnimationId = animation
return anim
end
return nil
end
function Animations:LoadAnimation(anim, char)
local animation = Animations.GetAnim(anim)
if animation then
if not char and game:GetService("RunService"):IsClient() then
char = game.Players.LocalPlayer.Character
elseif not char and game:GetService("RunService"):IsServer() then
warn("Server animations need a `char` argument to work!")
return nil
end
if char and char:WaitForChild("Humanoid") and char.Humanoid:WaitForChild("Animator") then
local animator = char.Humanoid.Animator
local animTrack = animator:LoadAnimation(animation)
return animTrack
else
warn("Animator or Humanoid not found!")
end
else
warn("Incorrect animation!")
end
return nil
end
return Animations
DataUtil - Not really necessary to post here, but using function :GetLocalData()
returns a table with a BoolValue named “Running”, which is used in some places in the Animate script.
Im currently unable to test with a completely default Animate script, since that would break my game. Not able to replicate this either. This never happened to me, and I dont think anyone has reported this bug to me either.
I know this is a long post, but this is a pretty big issue, and I cant find any solution. Help is much appreciated.