Why does this table turn to nil when trying to play an animation? (Custom animation module)

I’m making a custom animation module, basically a stripped down version of roblox’s default Animate script and modularised, with a few extra features such as detecting signals within the animation. While testing, I’ve noticed that the animtable property turns to nil at some point, and I’m not sure why. I have a function inside the util module required which prints the table and the animtable is correctly loaded. However, all my errors come from accessing the animtable to actually play the animations. Any help would be appreciated.

local player = game:GetService("Players").LocalPlayer
local repstore = game:GetService("ReplicatedStorage").repstore
local util = require(repstore.modules.util)

local animids = require(script.anims)
local signals = require(script.signals)

local animate = {}
animate.__index = animate


function animate.new(char)
	local root = char:FindFirstChild("HumanoidRootPart")
	
	--local hum = char.Humanoid
	--[[for name,anim in next,animids do
		local animation = Instance.new("Animation")
		animation.AnimationId = anim.id
		animation.Name = name
		animation.Parent = repstore.anims
		anims[name] = hum:LoadAnimation(animation) 
	end]]
	
	return setmetatable({
		char					=char,
		root					=root,
		rightshoulder			=char:WaitForChild("RightUpperArm"):FindFirstChild("RightShoulder"),
		leftshoulder			=char:WaitForChild("LeftUpperArm"):FindFirstChild("LeftShoulder"),
		righthip				=char:WaitForChild("RightUpperLeg"):FindFirstChild("RightHip"),
		lefthip					=char:WaitForChild("LeftUpperLeg"):FindFirstChild("LeftHip"),
		neck					=char:WaitForChild("Head"):FindFirstChild("Neck"),
		hum						=char:WaitForChild("Humanoid"),
		
		pose 					="idle",
		currentanim				=nil,
		currentidx				=1,
		currentanimspeed		=1,
		animtable				={},
		
		jumpanimtime			=0,
		jumpanimduration		=0.3,
		
		enabled					=true,
		preloaded				={},
		lasttick				=nil,
		currentsignalhandlers	={},
		
		loopertime				=1/10
		
	},animate)
end

function animate:setchar(char) self.char=char end
function animate:getchar() return self.char end
function animate:getenabled() return self.enabled end
function animate:setenabled(bool) self.enabled = bool end
function animate:getlasttick() return self.lasttick end
function animate:randomint(a,b) return Random.new(tick()):NextInteger(a,b) end
function animate:stopcurrent(time) self.currentanim:Stop(time) self:setpose("") end
function animate:getanim(name) return self.animtable[name] end

function animate:load(name,filelist)
	print("animate:load(",name,filelist,")")
	if self.animtable[name] then return end
	self.animtable[name] = { count=0,totalweight=0 }
	self.animtable[name].getcount = function(self) return self.count end
	self.animtable[name].gettotalweight = function(self) return self.totalweight end
	
	if self.animtable[name].count <= 0 then
		for idx,anim in next,filelist do
			self.animtable[name][idx] = {}
			self.animtable[name][idx].anim = Instance.new("Animation")
			self.animtable[name][idx].anim.Parent = repstore.anims
			self.animtable[name][idx].anim.Name = name
			self.animtable[name][idx].anim.AnimationId = anim.id
			self.animtable[name][idx].weight = anim.weight
			self.animtable[name][idx].looped = anim.looped 
			self.animtable[name].count += 1
			self.animtable[name].totalweight += anim.weight
		end
	end
	for i,animtype in next,self.animtable do
		for idx=1,animtype.count,1 do
			if self.preloaded[animtype[idx].anim.AnimationId] == nil then
				self.animtable[i][idx].track = self.hum:LoadAnimation(animtype[idx].anim)
				self.animtable[name][idx].track.Looped = animtype[idx].looped
				self.preloaded[animtype[idx].anim.AnimationId] = true
			end
		end
	end
end

function animate:setup()
	for name,filelist in next,animids do
		if type(filelist)~="function" then
			self:load(name,filelist)
		end
	end
end

function animate:stopall()
	local oldanim = self.currentanim
	if self.currentkeyframehandler then self.currentkeyframehandler:Disconnect() end
	if self.currentanim then self:stopcurrent() end
	return oldanim
end

function animate:setspeed(speed)
	if speed ~= self.currentanimspeed and self.currentanim then
		self.currentanimspeed = speed
		self.currentanim:AdjustSpeed(self.currentanimspeed)
	end
end

function animate:rollanim()
	if self.animtable[self.pose] == nil then return nil end
	local roll = self:randomint(1,self.animtable[self.pose].totalweight)
	local origroll = roll
	local idx=1
	while roll > self.animtable[self.pose][idx].weight do
		roll -= self.animtable[self.pose][idx].weight
		idx += 1
	end
	return idx
end

function animate:play(name,time)
	self.pose = name
	self.currentidx = self:rollanim()
	self.currentanim = self.animtable[self.pose][self.currentidx].track
	
	if signals[name] then
		for signalname,signal in next,signals[name] do
			if self.currentsignalhandlers[signalname] then self.currentsignalhandlers[signalname]:Disconnect() end
			self.currentsignalhandlers[signalname] = self.currentanim:GetMarkerReachedSignal(signalname):Connect(function()
				signals[name][signalname]()
			end)
		end
	end
	
	self:setspeed(1)
	self.currentanim:Play(time)
end

function animate:setpose(pose,time)
	time = time or 0.2
	if self.animtable[pose] then 
		animate:play(pose,time) 
	else 
		self.pose = pose 
	end
end

function animate:ondied() return function() self:setpose("dead") end end
function animate:onrunning() 
	return function(speed) 
		if speed > 0.1 then
			if self.hum.WalkSpeed > 16  then
				self:setpose("run")
			else
				self:setpose("walk")
			end
		else
			self:setpose("idle")
		end
	end 
end
function animate:onjumping()
	return function()
		self:setpose("jump",0)
		self.jumpanimtime = self.jumpanimduration
	end
end
function animate:onclimbing()
	return function(speed)
		self:setpose("climb",0.1)
		self:setspeed(speed/12)
	end
end
function animate:ongettingup() return function() self:setpose("gettingup") end end
function animate:onfreefalling()
	return function()
		if self.jumpanimtime <= 0 then self:setpose("fall",self.falltransitiontime) end
	end
end
function animate:onfallingdown() return function() self:setpose("fallingdown") end end
function animate:onseated() return function() self:setpose("sit") end end
function animate:onplatformstanding() return function() self:setpose("platformstanding") end end

function animate:step(t)
	local ts = self.lasttick and t-self.lasttick or self.loopertime
	self.lasttick=t
	if not self.hum then return end
	if self.jumpanimtime then if self.jumpanimtime > 0 then self.jumpanimtime -= ts end end
	
	if self.pose == "dead" or self.pose == "gettingup" or self.pose == "fallingdown" or self.pose == "seated" or self.pose == "platformstanding" then
		self:stopall()
	end
end


function animate.init(char)
	local newanimate = animate.new(char)
	newanimate:setup()
	local hum = newanimate.hum
	hum.Died:Connect(newanimate:ondied())
	hum.Running:Connect(newanimate:onrunning())
	hum.Jumping:Connect(newanimate:onjumping())
	hum.Climbing:Connect(newanimate:onclimbing())
	hum.GettingUp:Connect(newanimate:ongettingup())
	hum.FreeFalling:Connect(newanimate:onfreefalling())
	hum.FallingDown:Connect(newanimate:onfallingdown())
	hum.Seated:Connect(newanimate:onseated())
	hum.PlatformStanding:Connect(newanimate:onplatformstanding())
	util.printtable(newanimate.animtable)
	newanimate:play("idle",0)
	
	coroutine.wrap(function()
		while newanimate.char and newanimate.char.Parent do
			local _,t = wait(newanimate.loopertime)
			newanimate:step(t)
		end
	end)()
	
	return animate
end


return animate

That’s a lot of lines. But any more debugging information for when the animtable property turns to nil? Are you checking the right animate object in your local/server script?

I believe you made a typo in your init script. Shouldn’t it return newanimate which has the loaded animations instead of animate?

function animate.init(char)
	local newanimate = animate.new(char)
	newanimate:setup()
	---connection stuff
--old code
	--return animate
     return newanimate
end

This newanimate object should be the one that is already :setup() with the animtable loaded but you were returning the old initial animate object which by default has animtable set to nil. Perhaps this should do the trick.

Oh yeah, that was a typo. I tried changing it and nothing happened. I changed the setpose function to this:

function animate:setpose(pose,time)
	time = time or 0.2
	print(pose,time)
	util.printtable(self.animtable[pose])
	if self.animtable[pose] then 
		self.pose = pose 
		animate:play(pose,time) 
	else 
		self.pose = pose 
	end
end

Wow I’m an idiot. It was set to animate:play not self:play. Doing that completely fixed my issue :rofl: