Animations breaking: Offset/Rotation of key frames stacked when layering animations

Issue Type: Other
Impact: High
Frequency: Rare
Date First Experienced:
Date Last Experienced: 2021-02-24 11:30 am pst

Reproduction Steps:
Game link: FDG - Studio and Gym V 3.21 - Roblox
Steps to reproduce in this game:

  • Join the game (user must be a member of the group to join)
  • Open the emotes menu by pressing the smiley face icon in the top hub
  • Spam click emotes, they will play incorrectly. Easiest to see on Dab and Center Splits

You can also reproduce this by opening the mobile menu (little phone icon on the hub) and using the animations there at the same time as using the keybind animations (press 2-5 on the keyboard to set a style, then press a letter key) which is a popular choice for the most avid players to create more complex animation combinations.

Repro in studio:
I am not 100% sure I know how this bug occurs because it does not happen to all of my animations, however, I think it’s when playing overlapping animations on two separate animation tracks. So for instance one animation is animating the entire body on one anim track and another is just animating the arms.

Expected Behavior:
I expect the animations to override each other as normal instead of adding offset/rotation and changing the entire animation.

Actual Behavior:
Roblox is cumulatively adding the offset/rotation of animation played on different animation tracks. In my game, Focus Dance and Gymnastics, I run animations on multiple tracks depending on their use so that players can overlap them. Normally these follow the normal priority rules (action vs idle) and keyframe rules (empty key frames won’t be animated) and the order the animations are played in. This allows players to create combinations that were not possible with the single animations alone.

This behaviour is currently broken when using more than one animation track to play animations (which is necessary for proper replication on the client side).

Gif example:

The animations appear to be adding rotation/offset when combined or repeated instead of replacing the previously played animation (animations are the same priority). This is not the first time this bug has occurred, the last time was several months ago after an update that was then reverted.

Workaround:

3 Likes

Do you have a place file with an animation pairing that can reproduce this?

I have a flag enabled today that is aimed at improving performance of densely-keyframed animations, but it also fixes a long-standing bug where our animation blending was not correct (or even deterministic) summing weights across animations of different priorities. But the correct code is also less forgiving of animation track weights that don’t sum to 1.0, which could be the cause of this.

1 Like

I grabbed just the dab animation into a separate file. Note: this is older code (and I think it might be using the deprecated humanoid load animation function) but this also occurs in my newer project that is definitely not using deprecated functions.

Spam the button to reproduce.

animation bug repro.rbxl (26.9 KB)

1 Like

Am experiencing the same issue in all of my games. No updates on my end to gun animations recently, it seems to have broken randomly (as other users are also reporting).
Link: game link

1 Like

Also, this is not only for animations made by users, default jumping/walking is broken on the client-side, however, it looks normal from another player’s perspective.
(from what I’ve seen this only applies on people who have updated roblox today, those who didn’t aren’t affected by this issue)

1 Like

I am also currently having the same issue, animations used to be fine yesterday, but today the animations are offset and completely disconnected from the character.

Perfect, thank you. I am going to roll back this fix while I have a look at your example, and the pistol-firing example from the other thread.

Interestingly, all of the repro examples sent to me so far involve at least one animation that is comprised of two identical keyframes being looped to hold a static pose.

1 Like

Same thing has been happening with a lot of people, Hopefully this gets fixed soon:


image

So, there are a couple of things going on with this dabbing example: one that I can fix, and one that you should fix. The new weight blending is what is causing the translations (shoulder offsets) to accumulate. It’s because we expect AnimationTrack weights to sum to 1.0. The old code discarded tracks when tracks within a given Priority exceeded weight of 1. The problem (bug) was that the developers had no control over which track was discarded, it actually mattered which one loaded from the CDN and started playing first.

In your example though, multiple tracks are being blended because your script is creating a new dabbing AnimationTrack each time the button is clicked. I’m able to click fast enough to get 18 tracks going before the first one stops. While the old engine code doesn’t accumulate the translations, it does still play all of these tracks and replicate all of them to the server and to all other clients. In other words, if I did this in game, even with the flag off, I’m forcing all other players’ clients to constantly evaluate 18 copies of the dabbing track for my character, which is going to be a load issue on low-end devices as well as a lot of undesired animation event replication.

Ideally, what you want to do when the Dab button is pressed and the character is already dabbing is either ignore the redundant request or restart the animation by retaining a reference (variable) to the dabbing AnimationTrack and setting TimePosition back to 0. You could also stop the track, reload the dab and play it again, but it’s more efficient to just rewind the already-loaded track if it’s still playing.

1 Like

So instead of playing animations on top of others, we need to stop them and play them after the current is stopped. Also animations cannot overlap when Already made within the humanoid?

This would work in the case of the dab animation (and is how some of my animations do work), however, the rest of my animations rely on using multiple animation tracks in order for players to combine animations.

I was currently in the process of rewriting my animation system in order to fix a replication bug and part of the fix involved creating an animation track for every animation in the game and storing the references in a table. This was the only way for these animation combos to replicate accurately to the server AND the client which is a problem in the game currently. The system did not have duplicate animation tracks for the same animation, just one track for each animation loaded (and they don’t load until the player tries to use one for the first time).

So essentially one track had a player jumping with their arms in the air, (+ legs and torso animated), and the other track had an animation with only arms key frames. Previously pressing the jump animation first, then the arms animation would override the arms but not the rest of the animation. So the players could use different arms while jumping. These two animations were on separate tracks. Now when the player uses the two together, the arms end up behind the players back because the rotations of the two tracks are getting added to each other.

If you’d like to see that system in action I can film some clips or privately share the file, but it is unreleased gameplay so I’ll only post if necessary.

To give you an idea of what I mean:

Animation track 1 (just arms keyframes):
image

Animation track 2: (all key frames)
image

Together, (playing track 2, then track 1) correct behaviour.
image

Incorrect behaviour would move the arms behind the back and offset them. This is what was happening when the flag was set.

In this particular case of jumping + arm motions, what are the published settings for Looping and Priority on the two tracks?

Also, are any of the involved animations static poses (all keyframes the same, no actual animation)?

2 Likes

Our bug channel went crazy last night with reports of dance animations breaking in strange ways. They are all configured as Looping with Priority of Action

I can send some examples if you’d like?


Edit: had a proper read of the thread now I’ve had my coffee.

I always wondered what exactly AnimationWeight did, as it never seemed to have any effect for us. That makes sense that it was previously broken.

Making sure animation tracks weight sum to one is a bit of a big ask. Part of our core experience relies on multiple Action animations to blend. For example, a player can be dancing to music then decide to jump on their rollerblades (both animations Action priority). When the rollerblade animation happens, does that mean we have to modify the dance animation weight, then set it back again on completion? That’s a lot of overhead for us to manage in code.

Would it be possible to normalise the weights on Roblox end so they sum to one? So we can have a weight of 1 for the dance animation, then set the jumping animation weight to 10 and Roblox could normalise them to 0.09 and 0.91 respectively?

I will look into making sure we aren’t accumulating multiple animation tracks. That could be a big win for low-end performance.

I think this bug fix may need to come with an announcement and sometime to get our code up to speed.

Priority of both animations is set to “action”
No looping
The arms have two identical key frames, start and end, so yes its a static pose. The jump animation is not a static pose.

OK, good. The next question then: does the jump animation have tracks and keyframes for any of the same joints as the arm animation? And if so, do you expect the arm animation to override whatever the jump animation is doing with the arms, or do you expect them to blend?

I ask because what I’ve just seen in two of the pistol-shooting examples that were sent to me this morning is that they were playing two animations at weight 1.0, with the same AnimationPriority, but expecting one to override the other. The expected outcome in this case is for the animations to blend 50/50. The current animation system (with flag off) does not handle this case properly, it expects that when two or more animations are affecting the same joint that they are either different priority (if one is meant to override the other) or they have AnimationTrack weights that sum to 1.0 (or less). This is why our Animate script explicitly sets the weights of the walk and run animation tracks to make them add up to 1.0.

2 Likes

Was “last night” in your time zone between 10am and 1:30pm in pacific time? If so, it could have been this change. Otherwise it was something else.

Yes, what I’m realizing today is that there is a lot of mystery about how animations are meant to be blended in our system. In a nutshell, the intended behavior is for animation tracks of the same priority to do a weighted blend, based on the track weights, and for animation tracks of a higher priority to take precedence in their influence over joints that are also being animated by a lower-priority track, but yield any balance of total weight to lower-priority tracks if they have weight less than 1.0. This is done because we don’t have a separate blend stage for animations that are in the process of fading in or out, it’s all done with just the track weights.

And you are correct that the expectation is that developers will set track weights to sum to 1.0. The Animate script has always done this for walk and run, and I guess we sort of expected that to be the example that everyone looks to when they decide to code their own. But the examples I’ve seen today suggest otherwise–many developers seem to be playing animations all with weight 1.0, and there are different expectations regarding what the outcome should be. Some expect the most-recent animation to replace the others, some expect them all to blend equally. The reality is that they are supposed to blend equally, but there is a long-existing bug in the blend code that makes it look like the most recent animation has priority :-/ And code that relies on this is unstable, even though it may look correct in Studio most of the time.

Sort of. This is actually what the new system is meant to be doing when the flag is turned on, but only if the weights within a priority sum to greater than 1.0. But there is a bug in the accumulators that is making the animations look additive. This will be fixed.

Normalization is not done when the weight sum is less than 1.0, because this would break existing games that rely on being able to play weights less than 1 to do their own fading or weighted application of a pose. If we always normalized, one track with weight of 0.1 would play the same as a track with weight 1.0, or if we made 1 track a special case, then it could play at weight 0.1, but as soon as you add another track with weight 0.1, they’d both jump to 0.5, creating an unexpected discontinuity. So the compromise we arrived at in order to fix the bug, but still allow less-than-full weight application of tracks without requiring a dummy “identity track” to take up the balance, is to do additive blending up to a weight sum of 1.0, and weighted blending if the tracks sum to greater than 1.0.

2 Likes

Yes, we’re in +10 time zone, so 10am Pacific would be 4am our time. Sorry I wasn’t clear.

We definitely are setting all our animation weights to 1, as my tests always seemed to indicate that field didn’t seem to work consistently. We may need to think about how to refactor based on this.

Yes, the jump animation has keyframes for the arms. I expect running a second animation of the same or greater weight with key frames on the arms to override it. This was the behaviour I was used to.

Also the reason all these animations have the same weight (action) is because they are expected to play overtop of core, idle and movement animations. If there was another option, I’d set the weight of the arms greater than the body animations, but that would put the body animations on the same weight as the walk animations. If we had more options than just core, idle, movement and action for weight settings, I think that would help, but even then, I’d have to go back and edit the weight on 80+ animations created over the last 4 years. That’s not ideal.

The intended behavior is that running a new animation of a higher priority will override the influence of one with a lower-priority, no matter which is started playing first. The observed behavior of one track being able to override another at the same priority is an artifact of an addition mistake in the blending logic. What is particularly unfortunate about this bug is that it’s preventing tracks within the same priority from doing a correct weighted blend as was originally intended.

You might be wondering now if there is a way to preserve this current, incorrect, but relied-upon behavior. Unfortunately, there are a few reasons it needs to be fixed. One reason is that it’s very confusing and breaks the contract that we publish regarding Enum.AnimationPriority. From AnimationTrack | Documentation - Roblox Creator Hub

Where two playing animations direct the target to move the same limb in different ways, the AnimationTrack with the highest priority will show. If both animations have the same priority, the weight of both tracks will be used to combine the animations.

For track weights that nicely sum to 1.0, the above statement actually holds true. But when the weight sum exceeds 1.0, incorrect things happen. Existing developers may just see this failure to do what is promised on the tin as how things have always been, but imagine that you’re brand new to Roblox development, and you want to blend two tracks. You give two tracks the same weight, and the same priority, start them both, and what you see is only one of the animations play :face_with_raised_eyebrow:. Basically, the bug results in two animation tracks not having the same evaluation priority, even when the developer has explicitly assigned them the same priority.

If you happened to want the second track you started to stomp over the first… you get what you want. But what if you actually want two or more tracks to do a weighted blend, like for an 8-way motion controller? Well, it’s currently possible, but only if you write code to manually manage the track weights to sum to 1.0, by adjusting all the tracks’ weights any time one is started or stopped. Developers who want the correct, intended behavior have voiced their opinion that this is quite tedious to implement. And indeed it was never meant to be this complicated, and in fact doing this kind of manual weight management generates an undesirable amount of animation event traffic.

The second, less obvious reason this bug has to be fixed is that it puts huge constraints on our animation system, and we need to scale the system’s performance to higher player counts and more sophisticated animation blending setups. Preserving the playback-order priority would lock us in to requiring that animation start/stop/weight events always be replicated in such as way that guarantees their ordering on all clients. Otherwise, different players would see different tracks being overridden. This preservation of event ordering can be a bottleneck, and it’s the type of constraint that you want to avoid in a real-time client-server system, whenever possible.

There are more reasons, like having competing explicit and implicit priorities makes for unreadable and error-prone code, and also because one optimization we’re doing to speed up the animation system is to reverse the order in which tracks are evaluated. They are currently evaluated from low to high priority, but the optimal way is from low to high priority, since this allows skipping the evaluation of lower-priority tracks that we know (after evaluating the higher ones) won’t contribute to the end result.

All that said, It is definitely possible to do jumping with different arm animations. The standard way is to group tracks by priority like this:

Core: Roblox animations
Idle: Character Idle animation loops
Motion: Walk, Run, Swim, Jump, etc… often a weighted blend between multiple tracks
Action: Tool use, weapon swings, other top priority animations meant to play over idle and motion.

The most common setup is for the jumping to have Motion priority, and the arm movement to have Action priority. That’s how most developers do things like running and jumping + weapon and tool arm actions. But, it’s technically possible to do it all within one priority, just cumbersome in that you’d likely need to either divide up animations into upper and lower body, with no overlap (not recommended).

If you don’t use Roblox default animations at all, it frees up the Core priority for developer use. This lets you essentially shift everything down, so that Idles are now playing at Core priority, and you have either two levels or motion or action at your disposal.

One last option we will consider, is to add more levels of priority. I don’t know the history behind why there are just 4 enumerated priorities, but I see no obvious reason why this could not just be any integer, removing this limitation altogether. We’ll discuss internally and see if there is some magic solution to this that would save you having to republish all those animations.

4 Likes