• Bugs
  • Libgdx Attachment Bug Fix Proposal

Related Discussions
...

Just a heads up.

The update for the latest libgdx framework 2 days ago leads to a bug where 'null' set attachments on the first frame of a timeline do not successfully 'hide' on animation state change.

I have temp fixed this for my project by reverting this line of code in the animation.java file:

skeleton.slots.get(slotIndex).attachmentName = attachmentName;

Reverted to:

skeleton.slots.get(slotIndex).setAttachment(attachmentName == null ? null : skeleton.getAttachment(slotIndex, attachmentName));

I hope you find time to solve this issue for others 🙂

Yeah, we just reverted that change in the master branch. You can safely update!

8 días más tarde

I still have the same problem even though I updated the latest runtime.

My situation is that I have two animations, guard and idle. At the beginning of the guard animation, I make a shield image visible in Spine and when there is animation changing, I make the shield image invisible by programming by setting null to the shield image attachment with Spine runtime.

Here is my code simplified version:

// set the guard animation
animationState.setAnimation(0, "guard", loop);

// some time later...
skeleton.findSlot("guard-shield").setAttachment(null);

// then change to idle immediately
animationState.setAnimation(0, "idle", loop);

// then update the animation
animationState.update(delta);
animationState.apply(skeleton);  // this line makes "guard-shield" on, but I don't want it to be so...

So, I reverted to the old code in the animation.java file which works fine.

public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
float[] frames = this.frames;
if (time < frames[0]) {
   if (lastTime > time) apply(skeleton, lastTime, Integer.MAX_VALUE, null, 0);
   return;
} else if (lastTime > time) //
   lastTime = -1;

int frame = (time >= frames[frames.length - 1] ? frames.length : binarySearch(frames, time)) - 1;
if (frames[frame] < lastTime) return;

String attachmentName = attachmentNames[frame];
skeleton.slots.get(slotIndex)
   .setAttachment(attachmentName == null ? null : skeleton.getAttachment(slotIndex, attachmentName));
}

For my project this works ok, but isn't it good?

This is your setup, right?
And you want the attachment to be turned off again at the point marked with a star?

Image removed due to the lack of support for HTTPS. | Show Anyway

I'm just pasting my reply from the other thread here:

@[borrado]
I think yours is a separate issue though the recent changes above does solve part of the issue, so it's cool.

It certainly wouldn't be ideal if the setup pose defines the slot to have no attachments active, and you have a lot of animations. Muddying up the dopesheet is never a good workflow.

Clarification: It's technically the slot's state and not the attachment's (this is why the key icon is next to the slot in Spine editor).
Setting the slot's attachment to null means "hiding an attachment".

Also, Spine's behavior is that it leaves things alone (bones, slots, draw order), unless an animation keys it. So its state actually already "remains" unless something else changes it. The fact that your higher tracks (or any of the other tracks) never key the slot back to empty is why the attachment doesn't disappear. This is just by default and can be changed with code.

You clear a slot by saying slot.Attachment = null. You return it to setup pose by saying slot.SetToSetupPose(). You could conceivably do that whenever an animation starts or ends (using the existing animation callbacks). Spine-Unity actually has some extra methods that make it easier to find the right items to reset.
So, normally, if you're animating on a single track, this would be as simple as resetting slot/bone states every start of an animation, or resetting animated item states every frame during transitions.

If you're on multiple tracks, it sort of requires case-by-case handling. But this is more manageable if certain slots are sort of "managed" by a track: For example, if animations on a specific track key that slot's attachment (or turn it on or off), or leave it alone for a lower track to animate.

If you could describe your setup in more concrete terms, we may have something simple to recommend. I think opening a new topic would be best. 🙂

I'll just reiterate. The current default behavior of the Spine runtimes is that it does nothing you don't tell it to do (through animation keys or through code). So when an animation keys a slot attachment, and nothing else nulls the slot, the attachment will stay active in that slot. It will not automatically go back to setup pose unless you tell it to.

I could be wrong (it really depends on your animation setup), but you can use the animation callbacks to react to an animation ending on track 1, and then resetting the slot there to setup pose slot.setToSetupPose() the frame after.
I'm not sure if the java/libGDX syntax, sorry! (Events syntax is one place where C# and Java differ a lot.)

Thank you for taking your time, but no, you misunderstood my situation. I believe the issue BinaryCat and I talking about is the same. I made the explanation picture and hope you understand it now.

The current default behavior of the Spine runtimes is that it does nothing you don't tell it to do

I do understand this behavior, that's why I keep saying that the shield should be off because I tell it to do so before changing to a new animation! Otherwise I need to make the shield off at key 0 in the idle animation in the above explanation.

And above all, this issue brought by updating to the latest runtime recently and reverting the code which I posted a few comments back works well.

Ah, I get it.
Guard keys the shield on. Idle has no key for the shield.

Image removed due to the lack of support for HTTPS. | Show Anyway


When "guard" transitions into the "idle" animation, the guard animation is still being applied underneath the idle animation in order to mix the two. This is AnimationState's current design.
This means the attachment is still set to visible throughout the transition duration.
So if you set the slot's attachment to null any time before the transition is done, it will still be visible.

So your current options (without any changes to AnimationState and the AttachmentTimeline.apply code) are:
a) Set the slot attachment to null AFTER the transition duration is done. This will cause the shield disappear a bit later.
Or
b) Place keys to null the slot on all the animations that could follow the "guard" animation. This will cause the shield to disappear immediately.

Changes can be made to AnimationState and AttachmentTimeline.apply but you still need to decide where.

I'm inclined to say the old code was too unreliable in cases where you use multiple tracks keying the same slot, or transitions between two animations that key the same slot. But if the old code worked for you, it should be ok.

I still don't think this is the same problem BinaryCats had. I will move this to a new topic since I don't think this is the same problem OP had either.

Yes, your picture is what I'm talking about. Thanks. Your hand drawing is great by the way 🙂

a) Set the slot attachment to null AFTER the transition duration is done. This will cause the shield disappear a bit later.
Or
b) Place keys to null the slot on all the animations that could follow the "guard" animation. This will cause the shield to disappear immediately.

Well, I thought I would need to implement either way before asking at first, but both ways are too cumbersome to implement so I wanted to know whether there is other solution or not. Especially b) way is too hard to maintain animations. What if I add a new attachment? I need to go through all the animations to turn off. It's not acceptable at all.

a) is also hard because how can I know "the transition duration is done"? I need somehow to check the transition is done constantly whenever animation changed.

Can't you just ignore previous attachment "on" when you apply for changing animation? Most likely you can take care of "on or off" in your current animation when you change to a new animation programmatically.


@[borrado]
Sorry, I didn't see what you wrote at the end. Seems like I bothered many people due to posting to the different topics, my bad 🙁 So, should I open a new topic?

Pharan escribió

I still don't think this is the same problem BinaryCats had. I will move this to a new topic since I don't think this is the same problem OP had either.

How is it not???

He is doing SetSlot = null. So am I, the difference is he is doing it via code, I am doing it via animations.

We both interrupt an animation that keys an attachment on. We both, at the same time, turn that attachment off. But then, due to mixing the slot turns back on.

The bug extends much further than that. but seen as you cant understand my problem, I wont go into it,. and giving him advice to wait until mixing has finished, is not always possible, Niether is "b) Place keys to null the slot on all the animations that could follow the "guard" animation. This will cause the shield to disappear immediately."

its the same :bang: bug.

BinaryCats, it is definitely similar but it isn't perfectly clear yet if it's the exact same bug, as applying a 1 frame animation to change the attachment could be a bug in mixing, whereas yookunka's problem doesn't involve a bug


AnimationState is working as designed. Pharan will get back to you in your thread though, we won't let your issue get lost.

It has come up a few times that keys happening during mixing can be problematic. Eg, your animation fires events and you listen for these at runtime to play a sound, spawn particle effects, etc. In some cases you want events for the animation being mixed out to be fired during mixing, sometimes you don't. It seems this is also true for attachment keys.

One solution would be for TrackEntry to provide a "threshold" for events and anything else that needs it. This would be set from 0 to 1 and is the percent of the mix time to continue firing the events (or applying the attachment keys) of the animation being mixed out. Eg, a threshold of zero would never fire events during mixing.

We have an issue for the event threshold (#4):
AnimationState improvements · Issue #621 · EsotericSoftware/spine-runtimes · GitHub
I've added an item there to find a solution to attachment keys during mixing (#12).


Please see here for a code change solution:
One Frame Attachment Animations

Thank you Nate, that solution worked very well. I'll use this code for the time being until you guys implement #12 in the future 😉