• Bugs
  • Bug with MeshAttachment.cs and Slot.cs (Unity Runtime)

So, I encountered some odd behavior when using the Hero skeleton that comes with the example assets with the spine Unity runtime package, and upon digging, I think I found a bug, with a proposed fix, well, at least what I changed in the Slot.cs class to make it behave correctly. The behavior was that the sword that the Hero holds shrinks, then grows, when switching from Idle to Run animations. You'll almost always get this if you step into unity with the Hero loaded in with the default (Idle) animation, then in the first possible Update in a script, switch it to his Run animation, then step into the next frame (after the Render) and you should see his sword shrink.

Here's some pictures to show this behavior:

This is the hero in his Idle post in the first rendered frame:

Изображение удалено из-за отсутствия поддержки HTTPS. | Показать

Then, this code is executed

_skeletonAnimation.AnimationName = RunAnimationName;

Then, once the skeleton animation is updated, i.e Mixing is applied (since Idle was the previous animation)

Изображение удалено из-за отсутствия поддержки HTTPS. | Показать

You can see the sword has mysteriously shrunk.

What I think is happening when the Hero is loaded in, the Idle animation data is loaded, and the MeshAttachment is added to the "weapon" slot. However, when the animation is switched to Run, a FFDTimeline is executed via the Mix() function in the Animation.cs, which attempts to alpha blend the attachment in the weapon slot's vertices. This is where I think the bug is creeping out.

I found that in the Slot.cs, it seems to cache the attachments vertices (if it is a mesh attachment) in the attachmentVertices variable. However, this variable is zero initialized, and never gets updated when a Mesh attachment is actually added.

public Attachment Attachment {
   get {
      return attachment;
   }
   set {
      if (attachment == value) return;
      attachment = value;
      attachmentTime = bone.skeleton.time;
      attachmentVerticesCount = 0;
   }
}

This means the animation will alpha blend the zero initialized vertices (due to not being set) with the previous swords animation (Idle) position. This causes the sword mesh to shrink due to the alpha blending, then grow. The problem is, if this animation gets overridden, the mixing stops and the sword could possible stay shrunk if it does not have enough time to blend/grow.

I grabbed the actual vertices of the sword mesh, and the vertex blending looks like this

[0]: 0 -> 1.795f
[1]: 0 -> -0.3393999f
[2]: 0 -> -0.364900023f
[3]: 0 -> -0.3464f
[4]: 0 -> -0.367200017f
[5]: 0 -> 0.3434999f
[6]: 0 -> 1.7927f
[7]: 0 -> 0.3505f

Anyways, tl;dr the code change I made to get this to not happen is simple:

public Attachment Attachment {
   get {
      return attachment;
   }
   set {
      if (attachment == value) return;
      attachment = value;
      attachmentTime = bone.skeleton.time;
      attachmentVerticesCount = 0;
       if (attachment is MeshAttachment)
       {
           attachmentVertices = ((MeshAttachment) attachment).Vertices;
           attachmentVerticesCount = attachmentVertices.Length;
       }
   }
}

If the attachment is a mesh i.e has verticles, then set the attachmentVertices.

That way, when the blending occurs, it wont go from

[0]: 1.795f -> 1.795f
[1]: -0.3393999f -> -0.3393999f
[2]: -0.364900023f -> -0.364900023f
[3]: -0.3464f -> -0.3464f
[4]: -0.367200017f -> -0.367200017f
[5]: 0.3434999f -> 0.3434999f
[6]: 1.7927f -> 1.7927f
[7]: 0.3505f -> 0.3505f

i.e no change, so not shrunken sword, and if the animation gets override, the sword won't stay shrunk.

Related Discussions
...
  • Изменено

Thanks for looking into it thoroughly.
I actually had a similar misconception of what the slot.attachmentVertices did before.
But it's not actually caching the vertices. It's storing vertices of MeshAttachments or WeightedMeshAttachments that were deformed via FFD. If no FFD was applied to it, slot.attachmentVertices won't be used for rendering.

This modification to FFDTimeline's Apply method seems to work and fixes the problem you described.
https://github.com/EsotericSoftware/spine-runtimes/commit/17ba6e2354989473c74eddf5ae55dac6a572d10a

No need for any changes to Slot.cs. But you were right, it was mixing from the zero-initialized array.
(and this was a mismatch with the java runtime and this matches it better). Basically, the check was done in the wrong place.

I'll be updating the spine-unity.unitypackage later today.
Again, thanks for reporting and looking into it!

PS. Don't keep your fix. That will break MeshAttachments. FFD timelines modify the slot.attachmentVertices and if you give it the stateless attachment vertex array, it will overwrite it permanently.

Thanks for clearing up the usage of this variable, I had a hard time determining its purpose. Your change is much less intrusive, thanks for the commit!