• Unity
  • Blend Weirdness in Unity 3.5?

Related Discussions
...

Any idea what would cause this kind of jittering? Notice how the shell of the base pulls back in.

There's animations:

Open
Opened (Looping)
Shoot

It seems like the blend into Shoot is playing the last few frames of "open" rather than "Opened".

I've confirmed the animation for 'Opened' doesn't have the hitch mentioned in it. It doesn't even modify the bones of the shell piece.

Can you share your assets?
This may be the first case of an alternate use case for the new AnimationState.
My initial suspicion is that it's trying to mix with setup pose and setup pose has the raised shell on the base.

Also, in all likelihood, if you have a bit of a complicated setup working around the old limitations of AnimationState, it's possible that you have to change some bits of the code to remove those workaround. Not sure if that's applicable here.

Yep.

Seems like the new multiple-previous system of the new AnimationState doing weird stuff.
I'll show this to Nate and we shall gather as a mathematical conclave to emerge with a solution.
Or it could be a porting typo on my end, who knows? :p

For now, you may want to set the mix between Unfurl and Unfurled, and Unfurled to Attack to 0.
Though this is a legitimate mix case and you shouldn't need to do that. It's currently not doing the right thing.

Will-do!

This is what we call the "dip" problem. Say you have animations A and B with keyed values relatively far from the setup pose. They don't have to be the same values, but it makes it easier to understand, so they are the same values. You are applying A 100%, then you mix to B. A mixes from 100% to 0% while B mixes from 0% to 100%. The result is the value dips toward the setup pose, then back to where it was. The same thing can happen when fading from one color to another, in digital audio, etc.

We've tried special cases to avoid this, but it gets very complicated. Here's the closest solution:
http://n4te.com/x/911-F2sj.txt
The idea is if there's a higher timeline for the property AND there is no lower timeline for the property, A would get applied 100%, then B would get applied from 0% to 100% on top of that


voila, the value doesn't change during the mix. Unfortunately it falls apart when there are multiple mixes going on. Thinking through what goes on during multiple mixing is headache inducing!

As Pharan mentioned, you don't actually need to mix between these particular animations. Other situations may have this problem though. FWIW, the pre-3.5 AnimationState also had this issue.

Forgive my naivety,

Isn't it as simple as saying that each mix has a weight. Unless otherwise defined, the mixes are summed together then divided by their weight:
Mix Time 0
Bind Pose: [100]
A: [0]

Mix Time 10
Bind Pose: [0]
A [100]

So at Mix Time 5, Bind Pose is 50% and A is 50%. Straight forward.

Bind Pose: [0]
A: [100]
B: [0]

Then another mix goes from A 100 to B 100... straightforward, right? Bind Pose Remains Weight 0 at all times during that mix.

Cool, so when to use Bind Pose? Treat Bind Pose as 'filler' for when the total value of a TrackEntry is less than 100.

So let's say:

Track 0: (Looping Forever)
[A -> B] [B -> C] [C -> A]

Let's stay for the sake of argument that the 'power' (weight?) of Animation C is 50 at peak. That would mean that Bind Pose would leak in during the transition from B to C and C to A - but never show up otherwise.

During B->C it would look like

B: [100]
C: [0]
Bind: [0]

Then

B: [50]
C: [50]
Bind: [0]

Then

B: [49]
C: [50]
Bind: [1]

right up to

B: [0]
C: [50]
Bind: [50]

Similarly, if you had a TrackEntry with several simultaneous mixes, if the 'total' of the weight against a given bone for the multiple mixes is > 100, you never apply any Bind Pose weight.

Am I missing something? Does this have to do with the curves generated for the mixes?

The math behind this is more like Photoshop layers, where layers aren't aware of each other but higher layers only try to override the pixels below them by a certain amount.

So if you had:
A: 100% opacity
B: 100% opacity
BG: 100% opacity
Only A would be visible.

If you had:
A: 50% opacity
B: 100% opacity
BG: 100% opacity
You'd have A half-visible and B half-visible.

If you had:
A: 50% opacity
B: 50% opacity
BG: 100% opacity
All 3 layers would be visible. BG peeks through.

The weights logic you mentioned would entail extra processing from awareness of the total number of timelines affecting a certain target bone or slot color or whatever (this means looping over animations more times, storing percentages per target transformation per timeline), and more importantly, a different API. (how do you set or reasonably expect users to manage weights so they always add up to 100%?)

Good instinct though. The mental image always helps the math. But on either end, we need to think of the problem in different ways.

It might literally be as simple as conceptually running an invisible Track 0 that is always Bind Pose. Then creating a simple setting that allows you to specify if Bind Pose's track weight is blended to zero in the first animation that plays or not.

Basically, Track 0 is Bind Pose
Track 1 is whatever animation is playing by default - including lingering state changes.

Unless you blend Track 1 away via AddEmptyAnimation, Bind Pose never leaks through on a bone that's been modified historically?

I thought that was kind of how it works?

Nice explanation of weighted (I like to call "additive") vs layered animation system, Pharan! It gets slightly more complex, since an animation doesn't have to key everything, but explaining that would cloud up the core of how it works.

You could do additive when using Spine, but you'd need to write the code to apply timeline data. This thread shows an awesome mech game using additive and has some discussion about its downsides:
Code:HARDCORE the mecha game

Re: an invisible track that applies the setup pose, that is basically what we do but we avoid having to set everything (every bone, slot, properties of all constraints, etc) to the setup pose before applying track animations. We do this by passing a setupPose flag to the timelines to tell them they should mix with the setup pose. The first timeline for a property gets this set to true so they mix with the setup pose, the rest of the timelines get false so they mix with the pose from the lower track(s). This way we only mix with the setup pose for the timelines that need it, not all keyable properties, and we both mix with the setup pose and apply the first animation in a single pass. This solves animations making unwanted changes, but it is not related to the "dip" problem.

The dip problem is when mixing A -> B, A mixes out while B mixes in. When A is at 50% and B is at 50% you see the dip.

The dip problem comes from using a layered system. It didn't make it into the grand AnimState refuktoring but it's hard to fix and the old AnimState had the issue. Hopefully we can figure out a solution going forward. The try I explained above is pretty close.

Okay, so if we have

Layer -1: BIND
Layer 0: A -> B -> C -> D

In this case, Stowed -> Unfurl -> Unfurled.

It makes sense that the keys from 'Stowed' would blend with the BIND pose, since according to your suggestion:

The first timeline for a property gets this set to true so they mix with the setup pose, the rest of the timelines get false so they mix with the pose from the lower track(s).

That makes sense. A is mixed with the bind pose? Then when it plays B - 'Unfurl' - all of those timelines would be marked as normal timelines. Then when C - 'Unfurled', aka the idle pose, of the skeleton, plays, I would expect only keys which were in 'A' and not modified in B or C to be auto blending back to the Bind Pose.

Or am I getting the use of the setupPos flag backwards?

It sounds like you have it right, except there is no layer -1. The setupPose flag is per timeline, not per animation, though it's fine to think of animations as having a single timeline keying the same property when thinking it through.

Xelnath wrote

when C - 'Unfurled', aka the idle pose, of the skeleton, plays, I would expect only keys which were in 'A' and not modified in B or C to be auto blending back to the Bind Pose.

Probably in your example you wouldn't be mixing A, B, and C at the same time. Instead you'd mix from A -> B and B -> C. If playing B and mixing to C, all of B's timelines are mixed to the setup pose, then C is applied on top. This is what causes the dip. The code I posted above tries to do as you suggest: 1) properties keyed in B but not C are mixed to the setup pose when B is applied, and 2) properties keyed in both B and C are applied 100% when B is applied, then overwritten at the mix% when C is applied. This does work, but has problems when mixing from multiple animations. I'm not sure I can even think that through. :p I'll give it a try anyway.

Imagine you are mixing C -> B using the method you suggest, then you interrupt that mix to play D. Now you are applying 3 animations: B then C then D. B is applied at 100%. Next, if C were applied at 100%, it wouldn't match how it looked when it was mixing in. So, we need to apply C at the mix% when it was interrupted, but this mix% won't change (we call this mixAlpha). Next, D is applied at its mix%. I'm not remembering off the top of my head what exactly goes wrong at this point, but after adding D, something is not at the same pose as it was before D and things snap to a different position.

The stress test is to have a few different animations that key the same and different bones, then to click them wildly. They should mix smoothly, nothing should snap, and they should end up in the right pose:

 Loading Image

Okay, this is starting to make sense. You are trying to make a deterministic solution that can be re-composed at-will, even if you decided to rewind time arbitrarily. Generally, a good call.

Just a quick clarification:

Why would there be any Dip if 'Track 1' is 100% A -> 100% B -> 100% C - no additional tracks used? Even if B's % is going down, C's % should be going up by an equal amount to prevent Bind Pose from being mixed in.

It would make sense if 'C' was on 'Track 2', - then B would be mixing 'out' with Bind Pose pose, then C is layered on top. However, if A, B and C are all on Track 1, they shouldn't be mixing with the Bind Pose - unless something in 'C' has no key and 'B' had a key. Right?

Imagine you are mixing C -> B using the method you suggest, then you interrupt that mix to play D. Now you are applying 3 animations: B then C then D. B is applied at 100%. Next, if C were applied at 100%, it wouldn't match how it looked when it was mixing in. So, we need to apply C at the mix% when it was interrupted, but this mix% won't change (we call this mixAlpha). Next, D is applied at its mix%. I'm not remembering off the top of my head what exactly goes wrong at this point, but after adding D, something is not at the same pose as it was before D and things snap to a different position.

If we imagine a puppet with Animation A (left hand down) and Animation B (left hand in the air), like your rotation example, and we interrupt at 50% mix between A and B, such that the left hand is halfway up (we'll imagine a CCW rotation for simplicity).

There's two 'intuitive' paths for handling the mix.

1) Continue all three mixes, blended together.
2) Snapshot the current position of the keyed frames and mix from that

These are two distinctly different use-cases.

1 makes the most sense in a scenario where we're 'stacking' lingering modifications, e.g. Walking (A), Running (B) and Jumping (C).
2 makes the most sense in a scenario where you're doing something sequentially and don't care about the past animations continuing as much as you do about the integrity of the new animation. E.g. Running (A), Jumping (B), then firing a Weapon (C).

The difference between these two use cases is basically this:

Method 1)

A Mix  [100............ 0]
B Mix  [0.............100]
C Mix               [0............ 100]

Method 2)

A Mix  [100.....20]*[100.............0]
B Mix  [0.......80]*[100.............0] 
C Mix               [0............ 100]

Which one is in place right now?

20 dana kasnije

Sorry, I missed your response.

Xelnath wrote

Why would there be any Dip if 'Track 1' is 100% A -> 100% B -> 100% C - no additional tracks used? Even if B's % is going down, C's % should be going up by an equal amount to prevent Bind Pose from being mixed in.

The poses are not additive. Consider, at 25% of the mix duration when mixing from A to B:
1) Pose skeleton 75% between A's pose and the setup pose.
2) Pose skeleton 25% between its current pose and B's pose.
Let's say A and B key the same value which is 100. Step 1 results in 75. Step 2 results in 75 + (100 - 75) * 0.25 = 81.25. This is the "dip". It is worst at 50%, resulting in a value of 75. The further from the setup pose the keyed value, the bigger the dip.

Xelnath wrote

It would make sense if 'C' was on 'Track 2', - then B would be mixing 'out' with Bind Pose pose, then C is layered on top. However, if A, B and C are all on Track 1, they shouldn't be mixing with the Bind Pose - unless something in 'C' has no key and 'B' had a key. Right?

We have tried something similar to this, as described above. Unfortunately it causes snapping when mixing multiple animations. We have not been able to find a stable solution.

Xelnath wrote

[Re: mixing multiple] Which one is in place right now?

If you are mixing A -> B and during the mix you go to C, A -> B continue their mix and the result is mixed to C.

Nate wrote

Sorry, I missed your response.

Xelnath wrote

Why would there be any Dip if 'Track 1' is 100% A -> 100% B -> 100% C - no additional tracks used? Even if B's % is going down, C's % should be going up by an equal amount to prevent Bind Pose from being mixed in.

The poses are not additive. Consider, at 25% of the mix duration when mixing from A to B:
1) Pose skeleton 75% between A's pose and the setup pose.
2) Pose skeleton 25% between its current pose and B's pose.
Let's say A and B key the same value which is 100. Step 1 results in 75. Step 2 results in 75 + (100 - 75) * 0.25 = 81.25. This is the "dip". It is worst at 50%, resulting in a value of 75. The further from the setup pose the keyed value, the bigger the dip.

No worries. Okay, so here's my perspective on where the logic falls down:

The poses are not additive. Consider, at 25% of the mix duration when mixing from A to B:
1) Pose skeleton 75% between A's pose and the setup pose.
2) Pose skeleton 25% between its current pose and B's pose.

This bold context only makes sense if A didn't finish mixing... or was first instructed to 'mix' with the Setup pose.

Using your example, let's take some arbitrary Timelines:

Setup Pose:

A: 0
B: 0
C: 50

Animation Alpha:

A: 100->100
B: 0 -> 100
C: Unkeyed

Animation Beta:

A: 100->100
B: Unkeyed
C: 0->100

In the sequence:

Bind Pose -> Alpha -> Beta

I would expect that the value of 'A' would never drop below 100.

1) The logic being that all timelines are attempting to blend between their current value and the next value according to the curve defined for each segment
2) If a timeline has a 'next value', it should use that value to blend out.
3) If a timeline has no 'next value', it should be mixing back towards the Bind Pose


Are these three hypothesis correct?


If not, the rest of this falls apart.
If yes, the following should happen:

During the Mix from 'Alpha to Beta':

At 50% mix, the value of 'A' should be 100. There is always a 'forward value' for the value of 'A'.
A should not be changing or blending back to the 'Bind Pose'.

By contrast, value 'B' should be at value 50 - there is no 'Forward Value' for the value of 'B' in any animation on that track. Therefore mixing with the 'Bind Pose' makes sense.

Value C, which has only the 'Bind Pose' value, would begin at 50 and remain at 50 until the Mix with B begins.

At that point, it would 'awaken' and begin a mix from Bind Pose with Beta, which would cause it to 'dip' from 50 to 0, then would resume from whatever value it had at the end of the Alpha->Beta mix, towards 100.


This all seems fine, standard and correct. The only issues that would come into place would occur if:

During the Mix from 'Alpha' to 'Beta',
Timeline A attempted to mix with 'Bind Pose'.

This doesn't make sense at a 'common case' fundamental level, because a 'forward value' for Timeline 'A' exists in 'Beta'. There's no need for the 'Ghost' Mix, aka Bind Pose, to be involved in Timeline A.

It DOES make sense for the 'Ghost' Mix to be involved in Timeline B. Animation Beta has no key for Timeline B. Therefore, it makes sense that Timeline 'B', provided no 'Forward Value' by Animation Beta, thus while Alpha is being mixed out, B goes from 100 to 0 again.


Now, you've repeatedly mentioned 'additional mixing' being a challenge. So I'm going to assume that the 'snap' case occurs when a new mix is introduced during a blend between a real timeline and a Bind Pose value. Please clarify if not. 🙂

Let's assume, for instance that new animation 'Theta' is turned on at 50% of the 'Alpha->Beta' mix.

Let's say Theta does nothing but set the value of Timeline 'B' to 200.

Theta
A: Unkeyed
B: 200-200
C: Unkeyed

Following Continuous Curve Theory - that we never want to trigger a snap in value unless requested by the user - the ideal result is that:

Timeline B is at value 100 at the start of the Alpha-Beta Mix.
Timeline B is at value 200 at the end of the Theta Mix.
At no point is there a jump in value.

The way to achieve these three goals is the following:

When the 'Theta' mix starts, we need to observe the current value of Timeline B.

Timeline B was going from 100 to 0, due to the influence of the 'Ghost' mix which was causing 'Alpha' to blend out towards 'Bind Pose'.

At 50% mix, the value of Timeline 'B' would be 50.


Correct so far?


In or to achieve a smooth blend on Timeline 'B' from 50 to 200, it would be inappropriate to derive the new value of 'B' from a mix of 'A' and 'Theta' alone - that would cause your aforementioned 'snap'.

Similarly, if we continue the mix from 'Alpha' to 'Bind Pose' on Timeline B - that would cause your described 'Dip'.
Likewise, if we follow curve theory and say that the rate of change on Timeline B must respected during the mix, you also get your 'Dip' in the curve.

Argh, I wish there was an easy way to plot curves in this forum. It would make this illustration so easy.

However, if we make use of the 'Ghost' mix and suddenly change her from being an inferred data point (the result of a 50% mix from 'Alpha' to 'Bind Pose') to a Timeline value holder....

'Ghost'
B: 50-50

Now exists. Ghost is nothing more than a container for the 'current value' of a Timeline that had no 'Forward Value' and was mid-blend, when a new timeline is introduced.

Now, rather than mixing between (Alpha to Bind Pose) + (Bind Pose to Theta), The Alpha to Bind Pose mix is marked as 'completed' and instead the blending only occurs between 'Ghost' and 'Theta'.

Unless I'm mistaken, this results in values like:

... okay, I'm opening my spreadsheet. Be back in an hour. :nerd:


21 Nov 2016, 08:58


The above is what happens when you mix as currently.

This is what happens with 'Ghost' mixing by holding onto the prior for unfinished blend values until the blend completes.

I tried to attach my Excel sheet... but it wouldn't let me. Will try to find a way around this... google doc, etc.


21 Nov 2016, 09:02


https://drive.google.com/file/d/0B7Q_oKLGtaJjX2xpZjUxcmp2U3c/view?usp=sharing

There we go.

The key here is that 'Ghost' only hold onto interrupted mix values between Current Pose and Bind Pose. She doesn't do anything else.

What you have explained is correct.

There's a problem with creating a "ghost" snapshot so we can mix from that when an mix is interrupted: it's complicated. There are many properties that can be keyed and more are being adding. Storing a snapshot is complex so I would prefer to calculate the pose we are mixing from. That is when A -> B is interrupted by Z, we continue mixing A -> B and then mix to Z.

The other thing is how to know if there is a forward value. Currently for each timeline we calculate if the timeline is the first timeline to be applied to a property. If it is AND the animation is being applied with an alpha < 1 (whether from mixing/crossfading or TrackEntry alpha), then we know to mix with the setup pose. The experimental code does what you suggest by also calculating if we are the last timeline to be applied to a property. Ie, there is no "forward value" to mix to. We use this logic on line 229 here:
http://n4te.com/x/911-F2sj.txt
As the comment says, if there is no lower timeline and there is a higher timeline for the property, use full alpha. This works for a single mix of A -> B. It doesn't work when the mix is interrupted with another animation. I don't remember why and can't explain at the moment, it's been a looong day. Here's a version of Skeleton Viewer with that code though:
http://esotericsoftware.com/temp/skeletonViewer.jar
And here is a skeleton JSON that is good for testing:
http://n4te.com/x/918-mixing.json
Try bone1 -> bone1-dupe, which are the same animations, and you should not see a dip. Try the same with the normal Skeleton Viewer and you'll see a dip. For failure cases, try bone1 -> bone2 -> bone1. Also bone1 -> bone 90 -> bone180. bone1 -> bone2 -> bone3 is OK because they key different bones.

I think as long as we all agree the status quo: where A->B gets mixed with the SetupPose on keyed values within both A and B - is not intuitive or an acceptable default for long, then we're past the difficult part of the conversation. We now only need to find a solution that works.

I can even consider that mixing from A->B, then allowing the A-B to complete when mixing in C may even make sense - because all of those are user-requested behaviours. However, because the setup pose can naturally, appropriately, be so far away from the current state of the skeleton, it feels uncomfortable and confusing.

Also agree that 'only if I'm the last timeline' makes some sense - because then we are working towards the user's goal 'mix out of this animation and into the new one C without leftover modifications'.

Apologies, but I am weak with Javascript and it will take me some time to figure out the side-by-side differences with my CSharp version and try to implement them.


21 Nov 2016, 23:40


Is this what I should expect to see?


21 Nov 2016, 23:51


Ok got it working (previewer)

.... Looking at the code, isnt this just a bug where because 1 ->2 -> 1 means 1 qualifies as being 'first' its not getting added to the list as 'last' as well - or worse is being mixed from the original mix at full alpha when it shouldnt be?


21 Nov 2016, 23:55


Hmmm looking at it more...

... aren't both of these sections going 'backwards' up the linked list, when one you want one to be going backward and one to be going forward?

      entry = entry.mixingFrom;
      while (entry != null) {
         checkTimelinesUsage(entry, entry.timelinesLast);
         entry = entry.mixingFrom;
      }
   }
}

/** From last to first mixingFrom entries, sets timelinesFirst to true on last, calls checkTimelineUsage on rest. */
private void setTimelinesFirst (TrackEntry entry) {
   if (entry.mixingFrom != null) {
      setTimelinesFirst(entry.mixingFrom);
      checkTimelinesUsage(entry, entry.timelinesFirst);
      return;
   }


22 Nov 2016, 00:01


Looking at the code even more, if you're going from

A->B, and a key is only set in 'b', TimelinesLast won't be set for a value that was keyed in B, but not A, because the first thing it does is skip to the previous entry...

This is a... quite indepth topic already.

But could this be the reason why

SetAnimation("Pickup",0,false);
AddAnimation("Idle",0,false,0);

Where Pickup leaves the skeleton in the same pose as the start of idle.
Mix value Pickup-> idle = 0.

Results in when Pickup is nolonger playing (i.e. currentTrackEntry.animation.name != "pickup") the skeleton, for one frame, snaps to setupPose, then continues playing idle.

Maybe important to note, having a delay of -0.02 fixes this problem 99% of the time

I cant get it to happen with skeletonviewer, see:
Skeleton Viewer Unit Test

Okay, I think the best solution for this scenario is a simple change.

When you are mixing between any Timeline and the Bind Pose, if that Timeline get another forward key added to it, you freeze the mix % between the current Timeline and the Bind Pose until the new Track reaches 100%. Then you forget the mix %.

This special case only applies to the Bind Pose auto-mixing, prevents the 'Dip', since there's no mix between a forward mix - and preserves the current mix value for the 'baseline tracks' in a way that doesn't cause snapping and allows you to keep the multiplicative math the same.

Thoughts?