Why are my animations broken?

Hello there! I want to create a tool with working animations, however I have run into some issues.
On my screen, when I equip a tool, it works fine. However, when another person equips the tool, their animations appear buggy/glitchy on my side.

is a video of what it looks like. At the start I test my character’s animations and they work flawlessly, and when I test the animation on another account, from the perspective of my first account, they glitch quite a bit.

In my code, when the tool is equipped, I basically run this function with the names of two animations, that are played one after another, and then a third animation to act as an idle animation, that is looped and played after the second one.

function play2(anim, anim2, idleanim)
	if not active then return end

	local t1 = tracks[anim]
	local t2 = tracks[anim2]
	local t3 = tracks[idleanim]
	if not t1 or not t2 or not t3 then return end

	activeAnim = true

	t1:Play()
	t1.Stopped:Wait()
	if active then 
		t2:Play()
	else 
		return
	end
	t2.Stopped:Wait()
	if active then 
		t3:Play()
	else 
		return
	end
	current = idleanim
	activeAnim = false

end

There is noticeable glitching when I switch from one animation to the next. What can I do to fix this?

I looked on the Developer Hub and have not found a solution. I have tried to fix this issue with different ways and none of them have been successful so far.

Please help and thanks for your time! :heart:

3 Likes

I can’t offer an exact rationale for this behavior, but I’ll give my best guess for a lead.
Someone who is more knowledgeable can correct me and I’ll redact this post immediately if I’m just completely off the rails. :wink:

I would imagine it has to do with the structure of the network, and Roblox’s complex logic to smoothly interpolate the “best guess” position of other clients at all times.

We cannot see in “real time” on the internet due to latency. When Client A runs an animation, Client A must tell the server, and then the server must tell Client B about it. Client B will inevitably be running the animation at a later time than Client A is running it because the information has traveled so far. Possibly on the order of 300ms+.

When Client A switches to another animation, the position of Client A’s limbs will be interpolated to reach the first frame of the new animation. Client B will be interpolating those limbs from a different location, due to the first animation playing later for Client B, so the second animation is staggered. Since interpolation of limbs between animations always takes the path of least resistance, we see the musket flail wildly as the wrist rotates from an earlier position according to Client B.

But again, this is only my best guess. I’m hoping someone will blow my theory out of the water because otherwise this seems very difficult to work around.

I know the Animation Editor has a “Constant” easing style that removes the interpolation between keyframes, but I don’t think that’s exactly what you need.

1 Like

Thanks for the explanation. In the video, I run three separate animations.


The first animation ends up in this position.
The second one starts in the same position, and ends up in the following position

The third one is an “idle” animation, so it just continually loops the same keyframe that you see in the second image. The idea is to have a lot of animations start or end at the keyframe shown by the first image, so that they may be connected via the pose the character will make.

Since I am using multiple animation tracks, I don’t think that easing styles will help me, but thanks for showing me something new :smiley:

I have also tried a few different solutions but none of them works:

  • Having the client send remotes to the server each time it needed an animation done, and having the server run it (laggy, I know). This caused the same issue as here
  • Having the client send remotes to the server each time it needed an animation done, and having the server redistribute it to all of the clients, to have them do. This had some issues, and also it felt like a waste of resources for each client to remember 20+ animation tracks for each player that joins the game.

Does anyone have any idea on what I can do?

Is there a compelling reason to keep these three animations all separate?
I wonder if you could make a single animation that pauses when it reaches certain keyframes so that you wouldn’t have to load three to make this effect happen.

It wouldn’t serve to explain why this is happening to you, but you might get a different result!

I’m curious, when you played them from the Server, did the animations appear to glitch for both players, or just the observing player?

Is there a compelling reason to keep these three animations all separate?

Yes, so that I can mix and match different animations. I tried to use pauses by adjusting the animation speed to 0, however I had some issues so I just switched to looping a pose, which has worked well for me, so far.

Speaking about issues, I was trying to figure out why one of my animations wasn’t working, and I stumbled upon this:


I set the rightmost marker to 0:15 and the center marker to 0:07, however when I save it, it resets it to the one currently shown. I fixed this by removing the center marker. Could this be somewhat related to my other issues?

I’m curious, when you played them from the Server, did the animations appear to glitch for both players, or just the observing player?

I don’t particularly remember but I believe that it occurred for both.

An update: I have tried every possible solution that I can think of, including using AdjustWeight, AdjustSpeed, etc and none of it fixes my issue. Here is my current code and the result of it:

function play2(anim, anim2, idleanim)
	if not active then return end

	local t1 = tracks[anim]
	local t2 = tracks[anim2]
	local t3 = tracks[idleanim]
	if not t1 or not t2 or not t3 then print("not found") return end
	activeAnim = true
	
	stopTracks()
	
	t1:Play()
	t1:GetMarkerReachedSignal("Pause"):Wait()
	t1:AdjustSpeed(0)
	t1:AdjustWeight(0.00001)
	if active then 
		t2:Play()
	else 
		return
	end
	t2:GetMarkerReachedSignal("Pause"):Wait()
	t2:AdjustSpeed(0)
	t2:AdjustWeight(0.00001)
	if active then 
		current = idleanim
		activeAnim = false
		t3:Play()		
	else 
		current = idleanim
		activeAnim = false
		return
	end

end

In the video, you can see how the animations play perfectly, locally, but when other players play them they appear to attempt to try to reset to the default position, before launching the new animation.

I tried following the instructions on this post, specifically Option 2, but it still did nothing to fix my issue: Animation Engine - Runtime Changes and Fixes

Does anyone have any idea on what I can do about this?

Bumping to see if anyone has a similar problem or solution

I’m getting the same issue. Animations attempt to rest before resuming another animation. Wait() empty actually does wait for a split-second, and that’s my guess as to why Roblox tries to restart the animation after it’s finished. The work-arounds I’ve been able to find is using animationTrack.Length to finish the animation exactly when it ends.

That’s not the issue that I had. Roblox won’t restart the animation unless you have animationTrack.Looped set to true.

This is what worked for me:

SOLUTION

  • Modify the original code/animation I provided to use only one “active” animation, anim1, to switch between idle animations (basically remove anim2). Set its Priority to Active.
  • For the idle animations, set their Priorities to Idle. Play this animation at the same time as the Action Animation. Once the Active animation ends, the idle one will automatically overtake it and it will transition seamlessly.

I’d suggest doing this when you create the Animation in the Animation Editor, because I have heard that if you change the priority in a Local Script, it won’t replicate to other clients.

This issue made me lose a lot of hours and braincells, so hopefully this helps you out :smiley: !

3 Likes