- Modifié
Swapping a linked mesh attachment causes a flicker
I don't know what's causing this, but I don't think I'm syncronizing the new attachment to the deforms of the old attachment or something? idk what I'm doing wrong.
auto slot = cardDisplay->getSkeleton()->findSlot("cardback");
auto oldAttachment = (spine::MeshAttachment*)slot->getAttachment();
auto newAttachment = (spine::MeshAttachment*)cardDisplay->getSkeleton()->getAttachment("cardback", cardName.c_str());
slot->setAttachment(newAttachment);
//me messing around trying to fix it
newAttachment->setDeformAttachment(oldAttachment->getDeformAttachment());
cardDisplay->getSkeleton()->updateCache();
cardDisplay->getSkeleton()->updateWorldTransform();
newAttachment->updateUVs();
For the spine file, the cardback is the source mesh, and all other cards are sibling meshes that are linked to it.
Slot setAttachment
clears the deform. Usually the deform comes from an animation, so if you move your code to before the animation is applied, the new attachment's deform will be set by the animation.
Okay got it! So the weird thing is I'm triggering this all off of an event to synchronize with the card flip... Do you have any ideas as to how I can reorganize that accordingly?
Edit:
I was able to fix it like this:
cardDisplay->getAnimationState()->setListener([cardWeak = std::weak_ptr(cardDisplay), skeleton = cardDisplay->getSkeleton()](spine::AnimationState* state, spine::EventType type, spine::TrackEntry* entry, spine::Event* event)
{
if (type == spine::EventType::EventType_Event
&& event
&& "flip_down" == event->getData().getName())
{
auto slot = skeleton->findSlot("cardback");
auto newAttachment = (spine::MeshAttachment*)skeleton->getAttachment("cardback", "cardback");
//custom function for calling functions between game loops.
ThreadManager::queueOnMainThread([cardWeak, slot, newAttachment]()
{
if (cardWeak.lock())
{
slot->setAttachment(newAttachment);
}
});
}
});
bit dirty, but it works!
AnimationState apply
collects the events as things are applied, then it fires them afterward. It does this because if events where fired as it was applying things, then the event listener would be able to mutate the AnimationState when it is in the middle of applying, which could break everything (for example, if you clear a track that it is applying). Anyway, your code works because it sets the attachment at the start of the next frame, then the AnimationState is applied, setting the deform for the new attachment.
You could do it similar to your original code. The deform is a Vector<float>
:
https://github.com/EsotericSoftware/spine-runtimes/blob/3.8/spine-cpp/spine-cpp/include/spine/Slot.h#L113
Setting the attachment clears the vector, but keeps the same reference:
https://github.com/EsotericSoftware/spine-runtimes/blob/3.8/spine-cpp/spine-cpp/src/spine/Slot.cpp#L104
You could copy the vector values, set the attachment (which clears the vector), then set the values again.
Unfortunately we don't have a way of setting the attachment without clearing the deform. It might make sense to check if the deform is valid for the new attachment and keep it if so. This change could be surprising for existing code, though I have a feeling it won't affect very many people, if any. We'll consider it for 4.1.
Nate a écritAnimationState
apply
collects the events as things are applied, then it fires them afterward. It does this because if events where fired as it was applying things, then the event listener would be able to mutate the AnimationState when it is in the middle of applying, which could break everything (for example, if you clear a track that it is applying). Anyway, your code works because it sets the attachment at the start of the next frame, then the AnimationState is applied, setting the deform for the new attachment.
Gotcha. Yeah I ran into the tail end of this when trying to call .apply() from my event and causing a stack overflow.
You could do it similar to your original code. The deform is a
Vector<float>
:
https://github.com/EsotericSoftware/spine-runtimes/blob/3.8/spine-cpp/spine-cpp/include/spine/Slot.h#L113
Setting the attachment clears the vector, but keeps the same reference:
https://github.com/EsotericSoftware/spine-runtimes/blob/3.8/spine-cpp/spine-cpp/src/spine/Slot.cpp#L104
You could copy the vector values, set the attachment (which clears the vector), then set the values again.
Tbh, I'm a little tempted to give that a shot. What I'm doing rn works, but since my cards are gonna be pretty constantly destroyed and recreated from hand to hand, I'd kinda prefer to keep most of the logic encapsulated.
Unfortunately we don't have a way of setting the attachment without clearing the deform. It might make sense to check if the deform is valid for the new attachment and keep it if so. This change could be surprising for existing code, though I have a feeling it won't affect very many people, if any. We'll consider it for 4.1.
Yeah, that makes sense. I mean maybe it would make sense for it to not reset the deform in the case of linked meshes? Idk, seems like a hyper specific case for behaviour that's sounds as if it's not being super commonly used. have you considered adding an applyToAttachment or applyToSlot to allow portions of the AnimationState.apply() behaviour to be invoked on demand?
Ucenna a écritmaybe it would make sense for it to not reset the deform in the case of linked meshes?
I think that makes sense, it's just a gotcha if the change happens to affect existing code negatively.
Ucenna a écrithave you considered adding an applyToAttachment or applyToSlot to allow portions of the AnimationState.apply() behaviour to be invoked on demand?
AnimationState is such a complex beast already, we try not to add anything more to it!
We've decided to make the change to keep the deform when possible. It has been done in 4.0-beta for spine-libgdx and will be ported to the other runtimes soon. :beer:
https://github.com/EsotericSoftware/spine-runtimes/commit/14b317008b8a5157eca5010ab3f5a08ee2f78fe7