• Unity
  • Get number of attachment at runtime

Hi there,

Very simple question but I don't find the answer in the doc: I have a Slot "eyes", which contains 3 attachments. How can I access that info (the number of attachments) using Spine API? SlotData doesn't provide that info apparently.

Thanks!

Related Discussions
...

The attachments are always stored in the Skin.
You probably want:
(1)
Skin.FindNamesForSlot - gets you a list of strings that you would use with skeleton.SetAttachment(slotName, attachmentName) or skin.GetAttachment(slotIndex, attachmentName)

(2)
Skin.FindAttachmentsForSlot - gets you a list of Attachment objects that you would use with slot.Attachment = attachment

Depending on the skin, you may get fewer than what you see in the editor (eg, if you did not put attachments under some skin placeholders.)
If you are not using Skins in the Spine Editor, you can find all your attachments in the skeleton.Data.DefaultSkin

I really just need the number of attachment for a given slot name, I should have mentioned that in the first place. That is just so I can set up my UI sliders properly in the character creation. (e.g: if the slot "eyes" has 3 attachments, my slider will slide from 1 to 3, etc.)

I don't use the Skins in Spine Editor because every parts of my character are independently customizable and it seemed easier to handle his skin myself.

So, about:

skeleton.Data.DefaultSkin

, I don't understand the Skin class functions:

  • Most functions requires a slotIndex, which I don't have, I only have the slot name (e.g: "eyes").
  • FindAttachmentsForSlot requires a List of attachment, if I had that list, I would already have the number of attachments using List.Count...
  • etc.
    I don't really understand those functions, they don't even return values :/

Any way to get the number of attachments that a Slot holds, with a function that would take a Slot name as parameter and return something like an int or a List?

Ah, well here's a quick static method that just does that.

   public static int GetAttachmentCount (Skeleton skeleton, string slotName) {
      var skin = skeleton.Skin ?? skeleton.Data.DefaultSkin;
      int slotIndex = skeleton.FindSlotIndex(slotName);

  int count = 0;
  foreach (var key in skin.Attachments.Keys)
     if (key.slotIndex == slotIndex)
        count++;

//      // Count both active skin and default skin.
//      if (skin != skeleton.Data.DefaultSkin) {
//         foreach (var key in skeleton.Data.DefaultSkin.Attachments.Keys)
//            if (key.slotIndex == slotIndex)
//               count++;
//      }

  return count;
   }

To elaborate:
Skins just store attachments according to their name and the slot index.
You can get the slotIndex from skeleton.FindSlotIndex or skeletonData.FindSlotIndex. The above method removes that need for you.

As for FindNamesForSlot and FindAttachmentsForSlot, the lists you supply are actually output buffers/results buffers.
This is a fairly common pattern when methods have to return an indeterminate number of values, and is the same idea behind Physics2D.RaycastNonAlloc, where you provide the array or list where it will return multiple results. This is to reduce memory allocation pressure.

I guess we could be clearer about what that parameter is for. Thanks for giving us feedback!

Anyway, the sample use for that method would look like this:

public class GetAttachmentNamesSampleClass : MonoBehaviour {

   List<string> attachmentNamesResults = new List<string>();

   void Start () {
      var skeletonAnimation = GetComponent<SkeletonAnimation>();
      var defaultSkin = skeletonAnimation.Skeleton.Data.DefaultSkin;
      string slotName = "eyes";
      int slotIndex = skeletonAnimation.skeleton.FindSlotIndex(slotName);

  attachmentNamesResults.Clear();
  defaultSkin.FindNamesForSlot(slotIndex, this.attachmentNamesResults);
  int attachmentCount = attachmentNamesResults.Count;
   }

}

Jeez, thanks for the detailed explanations, they were greatly needed. I didn't realize it was an output situation, I'm not used to that pattern but I guess others won't have issues with it. Though I find it a bit odd to let a API function modify a List from the outside world, it feels like cheating encapsulation!
But yeah, whenever you have the chance and the time, please don't hesitate to add short summary as you build the API functions, that really helps! (I realize it's a heavy task though)

Anyway, thanks for providing a function that fits my need and explaining everything, it's perfect.

I don't think it's an encapsulation problem. If anything, it disobeys functional programming.
Providing a buffer for a function to fill is nothing new, but I guess it's more common in performance- and memory-sensitive low-level stuff.

A more recent example is that Unity 5.5.2 is providing new non-allocating Mesh vertex data buffer getters. And you do the same thing. Pass in a List<Vector3> and it fills it with the mesh data. Once again, this has the benefit of not having to allocate a chunk of the heap in case you had to call it every frame, because using the UnityEngine.Mesh.vertices property does do that.

Glad to hear it's working!
I've already added this API doc stuff to the list of things to do.