The animation plays visually but doesn't trigger events (Stopped/Ended/GetMarkerReachedSignal)

I have an Attack animation for my NPC. After I :Play() the AnimationTrack the animation fires 0 events. I made some debbuging and I found that the AnimationTrack Length is 0 even after 10+ seconds of waiting. I reuploaded the animation and I still get no length and no events that get fired. The animation priority is set to Action, Loop is not toggled on. Here is a snippet of the debugged code:

function State:OnEnter(data: NPCStateMachineData.NPCData)
	if not NPCStateMachineData.ReturnNPCHumanoidRootPartIfNPCDataHasGoodState(data) then
		return
	end
	
	ParametricFunctions.StopAnimationTracksFromTable(data.AnimationTracks :: any);

	self.AttackEndedConnection = data.AnimationTracks.DefaultAttack.Ended:Once(function()
		local nextState = HandleAttackEndedEvent(data)
		
		if not nextState then
			self:ChangeState(NPCStateMachineData.States.Patrolling)
			return
		end
		self:ChangeState(nextState)
	end);
	
	self.HitConnection = data.AnimationTracks.DefaultAttack:GetMarkerReachedSignal(NPCEnums.AnimationEvents.Hit):Once(function()
		HandleHitEvent(data)
	end)
	data.AnimationTracks.DefaultAttack:GetMarkerReachedSignal(NPCEnums.AnimationEvents.Hit):Once(function()
		print("hit1")
	end)
	
	data.AnimationTracks.DefaultAttack.Ended:Once(function()
		print("end1")
	end)
	data.AnimationTracks.DefaultAttack.Stopped:Once(function()
		print("stopped1")
	end)
	data.AnimationTracks.DefaultAttack.Changed:Once(function(propertyName)
		print("Changed1", propertyName)
	end)
	data.AnimationTracks.DefaultAttack.DidLoop:Once(function()
		print("DidLoop1")
	end)
	data.AnimationTracks.DefaultAttack.Destroying:Once(function()
		print("Destroying1")
	end)
	print(data.AnimationTracks.DefaultAttack.IsPlaying, "isplaying")
	local test = data.AnimationTracks.DefaultAttack
	data.AnimationTracks.DefaultAttack:Play()
	print("PLAY!")
	print(data.AnimationTracks.DefaultAttack.IsPlaying, "isplaying")
	print(test.IsPlaying, "isplaying test")
	data.AnimationTracks.DefaultAttack.Ended:Once(function()
		print("end2")
	end)
	data.AnimationTracks.DefaultAttack.Stopped:Once(function()
		print("stopped2")
	end)
	data.AnimationTracks.DefaultAttack.Changed:Once(function()
		print("Changed2")
	end)
	data.AnimationTracks.DefaultAttack.DidLoop:Once(function()
		print("DidLoop2")
	end)
	data.AnimationTracks.DefaultAttack.Destroying:Once(function()
		print("Destroying2")
	end)
	task.delay(10, function()
		print(data.AnimationTracks.DefaultAttack.IsPlaying)
		print(test.IsPlaying)
	end)
	task.wait(20)
	self:ChangeState(NPCStateMachineData.States.Patrolling)
end

this is the console:

this is the animation in animator:

1 Like

So the code I wrote for my NPC worked a few days ago. (The animation fired events). Now with the old code it doesn’t. I fixed it by Loading the animations inside the Animator later, not after cloning the Model, but when the Attack/Idle/Walk states appear, which has a little delay. I did it with this code:

Shared.LoadAnimations = function(npcData: NPCData)
	if not Shared.ReturnNPCHumanoidRootPartIfNPCDataHasGoodState(npcData) then
		return
	end
	if npcData.AnimationTracks then
		return
	end
	
	npcData.AnimationTracks = {
		Idle = npcData.NPCModel.Humanoid.Animator:LoadAnimation(AnimationInstances.IdleAnimation),
		Walk = npcData.NPCModel.Humanoid.Animator:LoadAnimation(AnimationInstances.WalkAnimation),
		DefaultAttack = npcData.NPCModel.Humanoid.Animator:LoadAnimation(AnimationInstances.DefaultAttackAnimation),
		Attack1 = npcData.NPCModel.Humanoid.Animator:LoadAnimation(AnimationInstances.Attack1Animation),
		Attack2 = npcData.NPCModel.Humanoid.Animator:LoadAnimation(AnimationInstances.Attack2Animation),
	}
end

It makes no sense to me this fix, if something is not working, I expect an error, not this. Just give an error like: The animator was not prepared to load animations, try again, or… The model is not fully loaded so you can’t LoadAnimations yet, or… idk. The Wierd part is that it worked before without this workarround.


Should I mark my answer as solution? :smile: I will not because I still belive it is a bug.

1 Like

Hi!
Just out of curiosity, is the issue maybe that the Animation assets aren’t loaded yet? Does anything change if you preload them?

local ContentProvider = game:GetService("ContentProvider")	
ContentProvider:PreloadAsync(AnimationInstances)
1 Like

I tried like this:

AnimationInstances module script
--!strict

local ContentProvider = game:GetService("ContentProvider")	

local NPCIds = require(script.Parent.Parent.Data.NPCIds)

local Local = {}
local Shared = {}

Local.AnimationInstancesArray = {}

for animationName, animationId in pairs(NPCIds.AnimationIds) do
	local animation = Instance.new("Animation")
	animation.AnimationId = "rbxassetid://" .. tostring(animationId)
	Shared[animationName .. "Animation"] = animation
	table.insert(Local.AnimationInstancesArray, animation)
end

ContentProvider:PreloadAsync(Local.AnimationInstancesArray)

return Shared

Same problem, the only solution is my workaround for now. Don’t ask me how I found the workaround :)) I stayed 7 hours trying diffrent things until I had the idea to delay a little the Loading of animations inside the Animator. Also, is there a problem with the fact that I load the animation inside the model that has a NIL parent? because I do all the preparation before spawning the model in the workspace (setting the parent to a folder that sits in the workspace): clone.Parent = SpawnedEnemies

1 Like

Found another solution! Which is better in my opinion. Put a force field on the model and place the clone of NPC in the workspace before loading the animations. It works like this also. I added a force field so the NPC has all the health and regen scripts assigned. SO the problem is that animations can’t be loaded properly on a model that has the parent nil. The thing is that it worked before. Maybe the old Version of Roblox studio was a little delayed and made the loading when the model was already in workspace. I will still leave this forum open because there is no proper error/warning for this case.

Code snippet:

Snippet
Shared.SpawnOnce = function(spawnArea: Part): (Model?, StateMachine.RobloxStateMachine?)
	local clone: typeof(Model) = Model:Clone()
	
	local waitedHumanoid = clone:WaitForChild("Humanoid", 7)
	if not waitedHumanoid then
		clone:Destroy()
		return nil, nil
	end
	local waitedAnimator = waitedHumanoid:WaitForChild("Animator", 7)
	if not waitedAnimator then
		clone:Destroy()
		return nil, nil
	end
	
	clone.Name = script.Parent.Name
	clone:SetAttribute("HostileNPCStringId", clone.Name)
	clone.HumanoidRootPart:SetAttribute("HostileNPCStringId", clone.Name)
	clone.Torso:SetAttribute("HostileNPCStringId", clone.Name)
	clone.Humanoid:SetAttribute("HostileNPCStringId", clone.Name)
	
	local cloneAttack1VisualHitBox = clone.VisualHitBoxes.WoodGolemCage
	ParametricFunctions.DeepSetTransparencyOfInstance(cloneAttack1VisualHitBox, 1)

	local clonePosition = ParametricFunctions.GetRandomPointOnCylinderSurface(
		spawnArea,
		clone.Humanoid.HipHeight + (clone.HumanoidRootPart.Size.Y / 2) + Constants.SPAWN_Y_OFFSET
	) :: Vector3

	clone.HumanoidRootPart.CFrame = CFrame.new(clonePosition)
	
	local forceField = Instance.new("ForceField")
	forceField.Parent = clone
	clone.Parent = SpawnedEnemies
	Debris:AddItem(forceField, 2)

	local npcData: NPCStateMachineData.NPCData & {[string]: any} = {
		NPCSpawnArea = spawnArea,

		NPCModel = clone :: any,
		NPCHumanoid = clone.Humanoid :: any,

		RandomInstance = Random.new(),

		VisualHitBoxes = {
			Attack1 = cloneAttack1VisualHitBox,
		} :: any,
		
		AnimationTracks = {
			Idle = clone.Humanoid.Animator:LoadAnimation(AnimationInstances.IdleAnimation),
			Walk = clone.Humanoid.Animator:LoadAnimation(AnimationInstances.WalkAnimation),
			DefaultAttack = clone.Humanoid.Animator:LoadAnimation(AnimationInstances.DefaultAttackAnimation),
			Attack1 = clone.Humanoid.Animator:LoadAnimation(AnimationInstances.Attack1Animation),
			Attack2 = clone.Humanoid.Animator:LoadAnimation(AnimationInstances.Attack2Animation),
		},
	}
	-- ... I can't reveal more

This is what I added:

local forceField = Instance.new("ForceField")
forceField.Parent = clone
clone.Parent = SpawnedEnemies
Debris:AddItem(forceField, 2)

This is what SpawnedEnemies is: local SpawnedEnemies = workspace.SpawnedEnemies

1 Like

Thank you! This was very helpful. I think I understand the problem now.

If you call LoadAnimation on an Animator that isn’t parented to the datamodel it will return an empty track that never properly loads the clip. This is a bug, what we want is for the track to work as intended once the Animator gets placed in the scene.

For now, you’ll need to defer calling LoadAnimation until after the character is parented to the dm. We’ll look into a fix for this on our end.

Here is server script that will reproduce your issue. If you run this and look at the server view you’ll notice the cloned character does not animate.

-- preload the animation so its ready to go!
local ContentProvider = game:GetService("ContentProvider")	
local animation = Instance.new("Animation")
animation.AnimationId = "rbxassetid://507766666" -- idle
ContentProvider:PreloadAsync({animation})

function cloneRig(srcRig, position)
	local clone = srcRig:Clone()
	local humanoid = clone:WaitForChild("Humanoid")
	local animator = humanoid:WaitForChild("Animator")

	-- adding to server storage before calling LoadAnimation will also work
	--clone.Parent = game.ServerStorage 
	
	-- This will not play, the animator is not in the datamodel
	local track = clone.Humanoid.Animator:LoadAnimation(animation)
	track:Play()
	
	clone.Parent = workspace
	clone:MoveTo(position)
	
	-- This will play properly, the animator is in the datamodel
	--local track = clone.Humanoid.Animator:LoadAnimation(animation)
	--track:Play()
	
	return clone
end

wait(5)
local clone = cloneRig(workspace.Rig, Vector3.new(0, 0, 0))
1 Like

I like to provide information that makes the error reproducible. I also found another bug I think… I will create a diffrent topic for it, but the new bug that I found is that the BreakJointsOnDeath was not working if the RequiresNeck was disabled. Is this a bug or normal behavior?

Hi, we’ve just deployed a fix that should correct issues when starting an animation before the animator is parented to the datamodel. Please check that everything is working on your end and let us know if you have any further questions.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.