- 수정됨
3.5+ Mixing carries over multiple animations...
...regardless of the set mix.
(this is using unity mix UI, so move this is you must, however I think it is more of a runtime design)
We have 3 animations:
Idle, Walk, and Attack
Which are played in quick succession: Image removed due to the lack of support for HTTPS. | Show Anyway
The mix time between Idle
-> Walk
= 0.2. The Mix time betweenIdle
->Attack
= 0, The Mix Time between Walk
->Attack
= 0.
Because the animations are played in quick succession, the ApplyMixingFrom
function shows that Attack
Is mixing from Idle
with a entry.mixDuration
= 0.2 (entry
= attack)
Changing line
entry.mixDuration = from.mixDuration;
to
entry.mixDuration = data.GetMix(newFrom.animation, entry.animation);
in UpdateMixingFrom (TrackEntry entry, float delta, bool canEnd)
in animationstate.cs works, however I am not sure this is the optimal fix
Does the problem occur in Skeleton Viewer? Can you provide the JSON and steps to reproduce in Skeleton Viewer?
well the code is the same soo.. I would say it would so. Skeletonviewer isn't exactly a good place to test stuff like this, you cant set up timings, or queues of events to fire.
I'm unsure how I can break this down any further.
0s: Play animation A
1s Play animation B
1.1s Play animation C
Animation A
->B
mix = 0.2s
Animation B
->C
mix = 0s
(Animation A
->C
mix = 0s)
Place a breakpoint inside ApplyMixingFrom
and observe it comethrough C
mixing from A
with a 0.2s mixDuration
I'd never get anything done if, for every forum post, I spent an hour setting up animations just to hope I can run into the exact same problem someone is having. Such a shot in the dark isn't efficient. If you can't repro in SV, how about providing the JSON and set/addAnimation
calls to repro?
Sure here is a unity project.
I assume you have visual studio. Put a breakpoint on line 128 in animation state, with the condition entry.animation.name == "walk3"
default mix = 0.
walk -> walk2 mix = 0.2.
(ignore the ik bug
names, I was trying to get a repo)
https://www.dropbox.com/s/lxgy52gzpk3s627/SpineIK%20Bug.zip?dl=0
open the goblins scene in SpineIK Bug\Spine IK Spinning\Assets\Examples\Other Examples
Pharan, can you take a look when you get a chance? :heart:
I'll check it out.
Link seems broken.
Link works fine for me.
I think some countries don't allow / doesn't work the https only http (atleast for Dropbox). I can't upload zip to Forum, too big
or maybe, some counties cant view the download page.
try https://www.dropbox.com/s/lxgy52gzpk3s627/SpineIK%20Bug.zip?dl=1 instead
Case:
Basically, it's ANY three animations A, B and C.
You play them in sequence quickly so that they are all in the middle of a crossfade/mix.
A->B->C
(where C.mixingFrom == B and B.mixingFrom == A
In AnimationState.updateMixingFrom (called from update)
, AnimationState handles the case where B->C mix ends before the A->B mix does.
In this case, we need to dispose of trackEntryB, and the new Track setup needs to be trackEntryC.mixingFrom == trackEntryA (a linked-list remove).
in other words
A->B->C
turns into
A->C
Code:
The block of code that handles it looks like this:
entry
is trackEntryC, from
is trackEntryB, newFrom
is trackEntryA.
TrackEntry from = entry.mixingFrom;
if (from == null) return;
if (canEnd && entry.mixTime >= entry.mixDuration && entry.mixTime > 0) {
queue.End(from);
TrackEntry newFrom = from.mixingFrom;
entry.mixingFrom = newFrom;
if (newFrom == null) return;
entry.mixTime = from.mixTime;
entry.mixDuration = from.mixDuration;
from = newFrom;
}
// [...]
The question is, what should happen?
In the current setup, when you dispose trackEntryB, you get its mixTime and mixDuration. (trackEntryB's mixDuration came from A->B mix data)
Because the resulting Track setup actually looks like trackEntryA->trackEntryC, @bcats thinks you should get the mixDuration of A->C from mix data and use that.
:wonder: that's what I said!
Thanks Pharan! :makeup:
BinaryCats wrote:wonder: that's what I said!
Riiight. :think: Also, I wanted JSON and set/addAnimation
calls and got a Unity project!
I can reproduce like this:
state.setAnimation(0, "walk", true);
state.addAnimation(0, "jump", true, 0.25f).mixDuration = 4;
state.addAnimation(0, "run", true, 1).mixDuration = 0;
There's also a similar test case where the run
mixDuration is 1 second. This is what the track entries look like:
walk
then: jump, 4s mixDuration
then, during walk -> jump mix: run, 1s mixDuration
walk.mixingFrom = null
jump.mixingFrom = walk
jump.mixDuration = 4
run.mixingFrom = jump
run.mixDuration = 1
jump to run completes:
walk.mixingFrom = null
run.mixingFrom = walk
run.mixDuration = 4
I noticed in the code Pharan pointed out that TrackEntry#mixAlpha is not copied like mixDuration
and mixTime
. However, I still see snapping when jump completes. My conclusion is that it never makes sense to remove an entry from the middle of the mixingFrom
linked list.
Let's say walk keys 0, jump keys 100, and run keys 200. walk to jump
goes 0 to 100 and jump to run
goes from that to 200. If jump to run
completes and we remove jump, we are left with walk to run
going 0 to 200. If walk to jump
was 50% then walk to jump
should have been 50, but after removing jump 50% becomes 100. This causes bones to snap when jump completes.
If we don't remove jump then it works correctly. walk to jump
keeps mixing from 0 to 100, then run is applied at 100% (since the jump to run
mix has completed). This example is a bit dumb because most likely run keys everything that walk and jump key, so completely overwrites their poses. However, it is possible to mix animations that don't key the same values, so it's possible that walk to jump
would be changing something run doesn't.
If removing from the middle of the linked list didn't cause snapping, it would be nice to avoid a long linked list when repeatedly interrupting long mix durations. It seems this optimization isn't possible though. I've committed the changes to spine-libgdx and they'll make their way to the other runtimes soon.
[libgdx] Only remove mixingFrom entries from the end of the list.@99ca32d
Nate wroteRiiight. Also, I wanted JSON and set/addAnimation calls and got a Unity project!
You seemed stressed. I thought providing pseudo code would have been enough. because unfortunately, I don't know all of the runtimes functions off by heart, and how they differ between languages. So I provided a unity project where you could see the code, and where it went wrong in an isolated environment. Rather than have you blame something else about the project.
I Specifically didn't provide more details about animations A
,B
and C
because I would have thought it would be obvious they could be any animations.
I did not know if add animation
rather than setanimation
recreated the bug, hence why I said:
BinaryCats wrote0s: Play animation A
1s Play animation B
1.1s Play animation C
I'm unsure how this is not obvious ¯_(ツ)_/¯
I am trying the fix now
confirmed works
BinaryCats wrote:wonder: that's what I said!
I used a better font.
Pseudo code wasn't enough, the problem was complex enough I needed to be able to reproduce it to work on it. That's why I asked for JSON and animation calls, which are the simplest way to reproduce and would ensure that what I am looking at is indeed the problem you are having. It's very common when helping users to have them send their whole project. This takes a lot of time to setup and dig through, and often the problem ends up being with the users code. It's also common for users to not provide enough information to reproduce the problem. It's time consuming to chase, guessing at their setups, skeleton data, etc and then hoping we see the exact same problem so we can fix it. By reducing things to their simplest form, users help us help them. Often they can find answers to their questions themselves.
Yes, it can be stressful. For Spine, if I get too much information (whole projects) then it usually gets backburnered, as I have a lot of things to do. If I don't get enough information, I have to ask for the right info which makes Nate angry!
For my OSS, too much or too little information usually means no response at all. I'm happy to help people with problems, but if they don't put effort into efficient use of my time, I'm less likely to spend my time. I never instruct those who aren't full of passion, and I never enlighten those who aren't struggling to explain themselves. If I show you one corner and you can't show me the other three, I'll say nothing more. -Confucius
Aaanyway, I'm glad the fix works! AnimationState is a beast.