Gizmo

Hi!

I recently tried to flip an animation in the Web Player. The project is basically a copy of the owl example to learn about the web player.
Now, the owl's directional animations each only have 1 (or 0?) frames, as you know. I tried to negate the x value of one bone's scale for e.g. the Right animation, and expected the result to be a mix between -x size and +x size depending on the mouse pos just as it works with the owl example's translation.

However, the changed bone is not able to flip back and forth, either it's flipped or not. Yet, when flipping a mesh/attachment, the blending works fine. It also works fine for animations longer than 1 (or 0?) frames, but blending between "still" animations is way more clean.

Could anyone tell me why this is the case, if this is necessary, or how I could flip my animation back and forth in the web player in another way?
Thanks for any help.

Edit: I have attached a video that shows what I mean. Spine shows the desired "Left" animation.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Gizmo
  • Сообщения: 4

Nate

You can mimic the owl's animation mixing using preview. For example, set idle to play on track 0, on track 1 enable additive (it's important you do this before setting the animation), set left to play on track 1, and then set the alpha on track 1 between 0 and 1 for how much of left gets applied. The alpha slider is often small, but you can drag left/right or up/down on the text box instead. Note that none of this solves your problem, I just wanted to mention it as an easier/faster way of exploring AnimationState tracks, mixing animations, additive blending, etc.

The behavior you see occurs because of the way the sign of the scale is mixed between animations. Mixing animations, whether between different tracks using alpha or between animations on the same track using mix duration, is a different situation than the interpolation between keys.

Between keys the interpolation between 1 and -1 scale would happen as you expect, the value changing smoothly from 1 to 0 to -1. If that is not desired, you can set the interpolation to stepped so the scale of 1 is held until the -1 key is reached.

When mixing animations and the sign of the scale differs between the animations, we've found it's rare to want the scale to be interpolated smoothly through zero. Doing that causes everything attached to the bone to get very thin as it approaches zero. Usually negative scale is used to have a bone axis point in the opposite direction for the pose in the animation. When mixing poses of two animations, usually you care about which direction the bone is pointed in each pose and don't want the bones flipping over like a sheet of paper.

Unlike when keying, when mixing you don't have the possibility to choose stepped, linear, or Bezier for each keyed property. We don't currently have a way to customize the behavior for the scale sign. The sign is always treated as an instant transition, like a draw order key or slot attachment visibility key.

The above makes sense for typical animation mixing between animations on the same track or across tracks, where the poses from the animations are the final poses you want when fully mixed (when alpha is 1 or the mix duration is reached). However, the owl uses additive mixing. Why would you use negative scale when mixing additively? Is the goal to have a bone axis point the opposite direction or to flip it over like a sheet of paper? I have a feeling it could make sense to interpolate the scale through zero when mixing additively. The relevant code starts here.

There are some other interesting challenges when mixing animations. For example, say the pose from each animation specifies the rotation for a bone and we want a rotation 50% of the way between those. There are two possible answers: the short way between the rotations or the long way between them. AnimationState chooses the short way, however the animations can change over time. The short way now may become the long way in a few frames! Once AnimationState has chosen the short way for a mix between animations, it continues using that same direction for the rest of the mix.

---

We've updated all 4.0 runtimes so when scale timelines are applied, the sign of the scale is not treated as an instant transition. The owl will now flip over as you expect. This change is also in the Preview view starting in editor version 4.0.32.
Аватара пользователя
Nate

Nate
  • Сообщения: 11345

Gizmo

Thank you for the answer, the tips and insight into the webplayer, I was surprised when I read that you have enabled a smooth transition. Tried it out immediately, but it still did not work out. I made sure to use the latest (4.0.*) web player script as instructed on GitHub and the files were exported for 4.0.. Not sure what else could have been the mistake here?
Gizmo
  • Сообщения: 4

Nate

Hard to say, can you post your owl HTML/JS?
Аватара пользователя
Nate

Nate
  • Сообщения: 11345

Gizmo

Yes I should have done that from the start, sorry - To make sure there are no mistakes I made a new smaller project with only one image, basically the owl example. Download: https://www.mediafire.com/file/x3s1fklq96cuh4b/scaling.zip/file.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Gizmo
  • Сообщения: 4

Nate

Thanks! Here's the code updated to behave like you want:
new spine.SpinePlayer("spine-container", {
jsonUrl: "res/skeleton.json",
atlasUrl: "res/skeletons.atlas",
showControls:false,
backgroundColor:"#00000000",
animation:"Center",
alpha:true,
showLoading:false,
viewport: {
x: -500,
y: -500,
width: 1000,
height: 1000,
padLeft: "0%",
padRight: "0%",
padTop: "0%",
padBottom: "0%"
},
success: function (player) {
setupAnimation(player);
},
error: function (player, reason) {
alert(reason);
},
frame: function (player) {
player.skeleton.setToSetupPose();
}
});

function setupAnimation (player) {
player.animationState.setAnimation(0, "Center", true);
left = player.animationState.setAnimation(1, "Left", false);
right = player.animationState.setAnimation(2, "Right", false);
left.mixBlend = right.mixBlend = spine.MixBlend.add;

document.addEventListener("mousemove", function (event) {
calculateBlend(event.clientX, event.clientY);
}, false);
}

function calculateBlend (x, y) {
x = x / (window.innerWidth);
y = (window.innerHeight - y) / (window.innerHeight);
if (x > 1) x = 1; else if (x < 0) x = 0;
if (y > 1) y = 1; else if (y < 0) y = 0;

left.alpha = (0.5 - Math.min(x, 0.5)) * 2;
right.alpha = (Math.max(x, 0.5) - 0.5) * 2;
}
The main issue was that the left and right track entries didn't have TrackEntry mixBlend set to MixBlend add. That applies the animations additively, so their values are added to the current pose. It also applies scale timelines differently, so the scale sign is not treated as an instant transition, as described above.

When applying animations additively, you need to reset the values they key, otherwise every frame the values are additive and your skeleton explodes. You can do this by keying the same values in an animation that is applied in a lower track. Another way to do it is to call Skeleton setToSetupPose every frame, before AnimationState apply. That is what I've done in the code above, using the frame callback for the Spine player.

I also multiplied your left/right alpha by 2, so the range is 0-1 instead of 0-0.5, and I moved adding the listener to after the skeleton is loaded, so you don't need to track if the skeleton is loaded.
Аватара пользователя
Nate

Nate
  • Сообщения: 11345

Gizmo

Thank you very much!
Gizmo
  • Сообщения: 4


Вернуться в Runtimes