• Unity
  • Keep Slots Visiblity Question

  • 編集済み
Related Discussions
...

I want to hide some slot's Attachment by setting them to null. For example hide arms and hands.

However some Animation has keys on these slots so that they will be visible again immediately.

What's is the best way to keep them hidden ?
Will it cost too much if I reset target slots' attachments every frame?
I have tried to make a runtime skin to store slot visiblity, but it doesn't work.

internal void AttachNull (Skeleton skeleton, Skin oldSkin, List<Slot> slots) {
    this.Append(oldSkin, false);
         
for (int i = 0; i < slots.Count; i++) { if (oldSkin.attachments.ContainsValue(slots[i].Attachment)) { this.SetAttachmentNull(slots[i].Data.Index, slots[i].Data.Name); //slot.Attachment = null; } } } public void SetAttachmentNull (int slotIndex, string name) { attachments[new AttachmentKeyTuple(slotIndex, name)] = null; }

Setting attachments to null is extremely cheap (just sets a field to null), so it's fine to do each frame after you apply animations. Or, if the attachments are under a skin placeholder, you could set the attachments to null in the skin. Hmm, I see we don't have a method to do that, we'll add one!

@[削除済み]
It actually does more than set a field. spine-runtimes/Slot.java at 3.7-beta

But yeah, it is cheap. But if it was necessary to do that every frame, I'm not sure if you might be better off not keying the attachment at all.

However, removing the attachment entries from the skin is pretty ideal. I think you were on the right track.

Note that the SetAttachmentNull method is not really necessary. Spine 3.6 and 3.7, you should have the needed extension method. Just make sure you have the AttachmentTools namespace added:

// above
using namespace Spine.Unity.Modules.AttachmentTools;

int slotIndex; // can get this from skeleton.FindSlotIndex
string keyName; // this is the name of the skin placeholder
yourSkin.RemoveAttachment(slotIndex, keyName); // Sets the skin entry to null.

// Any time you make changes to a skin and want them to be reflected on a skeleton immediately,
// you need to tell the skeleton to update its attachments based on the skin.
skeleton.SetSlotsToSetupPose(); // use setup pose attachments
//skeletonAnimation.AnimationState.Apply(skeleton); // Usually not required but certain use cases need this so attachments from the current animation's keys are immediately applied.

// Alternatively, if you know which slot you want to update, you can just update that slot.
slot.SetToSetupPose();
// or...
// slot.Attachment = skeleton.GetAttachment(slotIndex, slot.Data.AttachmentName);

An additional thing to consider is that if your skeleton's default skin has an attachment, setting the attachment value on the skeleton's active skin to null, or removing that value, will cause it to fall back to the attachment found in the default skin.

In the near future, there are runtime changes and API additions planned in the base runtime for this: [runtimes] Add API to create combined skin from multiple other skins · #841

Thanks, Nate, Pharan.

But yeah, it is cheap. But if it was necessary to do that every frame, I'm not sure if you might be better off not keying the attachment at all.

Some attachments have mesh deform keys which are unavoidable.
Actually I have a group of Slots to toggle visibility, [e.g. a head made up of 10 Slots], I prepare a maskSkin just to keep their relation.
Therefore I must go through a [For loop] ,setting each slot's attachment to null. I'm not sure if it's cheap enough.

The Skin way should only execute once

Skin maskSkin = data.FindSkin(maskSkinName);
if (maskSkin == null) return;

Skin customSkin = new Skin("customSkin");
customSkin.Append(this.UnshareSkin(false, false));

string keyName = "holder"; // this is the name of the skin placeholder
foreach (KeyValuePair<Spine.Skin.AttachmentKeyTuple, Attachment> entry in maskSkin.Attachments)
{
   int slotIndex = entry.Key.slotIndex;
   customSkin.RemoveAttachment(slotIndex, keyName);
}

this.SetSkin(customSkin);
this.SetSlotsToSetupPose();

An additional thing to consider is that if your skeleton's default skin has an attachment, setting the attachment value on the skeleton's active skin to null, or removing that value, will cause it to fall back to the attachment found in the default skin.

Thanks for pointing out this, maybe it's the problem why my code didn't work before.
Considering that, I make a Base Skin to show every thing instead of using defaultSkin.