• Unity
  • Help Pls!What causes this list went wrong?

My game needs me to split the body of the character into 5 parts (head/chest/ hands/legs) for different functions; therefore, I split the SkeletonData (ExposedList<SubmeshInstruction> workingSubmeshInstructions ) by using SkeletonRenderSeparator script. However, when I was trying to make one of the body parts of the character disappeared, an error popped out. It says there are wrong elements in the Slot Materials’ list which provided by the SkeletonRenderSeparator. So, my question is what causes this error? Did I do anything wrong?

Errors below:

Related Discussions
...

However, when I was trying to make one of the body parts of the character disappeared,

How did you make the body part disappear? You could just disable the MeshRenderer component on the part to be disabled, then it will not be rendered.

In general, you need a SkeletonRenderSeparator in order to insert other things visually between the parts (in terms or back to front layer order).

If you want to switch appearance of body parts, it is recommended to use Spine Skins instead of render-separator parts, since Skins are made for this purpose.
If you need things like other GameObjects, colliders, particle effects, etc follow your body parts, you could better use a BoneFollower component that follows the location of a specific bone.

Just to ensure you are on the easiest way to achieve your desired goal.

Harald escribió

I have already used SkeletonRenderSeparator to achieve my desired outcome; however, my real question is what might be the reason for causing the above Bug that I mentioned earlier. What caused it? Did I do anything wrong with the SkeletonRenderSeparator script?
To your question, the reason why that I could not just simply disable the MeshRenderer component is that I need to shift some body parts of my character; moreover, while my character is running some Spine animations, the character’s slot order is changing occasionally. Therefore, my method was removing the data of the slots’ attachment to achieve my goal.
Also, is there any other way to achieve my goal except using SkeletonRenderSeparator?

Did any error occur when applying the SkeletonRenderSeparator script as is? Or did you modify any of it's code?

Therefore, my method was removing the data of the slots’ attachment to achieve my goal.

Does your error still occur when you do not do this?
How did you implement what you described here?

Harald escribió

Does your error still occur when you do not do this?
How did you implement what you described here?

the error is not occurred anymore, after i'm not doing this. however, i can not meet my goal without using that method.

public bool isHide = false;
        private Attachment hideImage;
        public void HiddenSlot(bool isHidden)
        {
            if (isHidden)
            {
                if (Attachment != null)
                {
                    hideImage = Attachment;
                }
                isHide = true;
                Attachment = null;
            }
            else
            {
                if (hideImage != null)
                {
                    Attachment = hideImage;
                }
            }
        }

this is how i change my slot attachment

Slot[] slots = RoleData.spineGB.GetComponent<SkeletonAnimation>().skeleton.Slots.Items;
        foreach (Slot item in slots)
        {
            for (int i = 0; i < slotStr1.Count; i++)
            {
                if (item.ToString().Contains(slotStr1[i]))
                {
                    item.HiddenSlot(true);
                    item.A = 0;
                }
            }
        }  

this code showed how i call the function above.

is there any better method or options could help me out of this trouble?

If I understood your code correctly, then you have added the method public void HiddenSlot(bool isHidden) above directly in the Slot class. It is not considered good software-engineering practice in general to modify 3rd party code, if it can be avoided. Just think of the case when we release an update of the file, then all your changes will be overwritten and you have to apply them again every time.

Nevertheless, I do not think that the code above caused the problem, I rather asume that the cause lies somewhere else.
Did you modify other classes as well, or only Slot?

8 días más tarde

Sorry for taking so long to reply. I took your advice and ran a new test through re-importing your spine-unitypackage; however, by only changing the Slot.cs document (adding the code below), the error still appeared.

public bool isHide = false;
        private Attachment hideImage;
        public void HiddenSlot(bool isHidden)
        {
            if (isHidden)
            {
                if (Attachment != null)
                {
                    hideImage = Attachment;
                }
                isHide = true;
                Attachment = null;
            }
            else
            {
                if (hideImage != null)
                {
                    Attachment = hideImage;
                }
            }
        }

Now, what should I do to achieve my goal on hiding the slot and avoiding the error at the same time? Any good suggestions? Thanks!

I wonder if the line item.A = 0; is not already sufficient to achieve your goal, have you tried that statement alone without the item.HiddenSlot(true);?

Apart from that, we in general suggest to not modify thirdparty code as described above, but instead perform the necessary steps in your own code.

Yes, I tried to use item.A = 0 to achieve my goal without changing your code, but when my character ran another animation, item.A = 0 changed to item.A = 1. Is this normal? Do you have any other solution to it? If the change from 0 to 1 was a mistake, then where did it go wrong? Is it possible that I did something wrong either in spine or unity?

Since the animation also sets the item's alpha value, this is considered normal, since you and a newly started animation are setting the same value, the last change will win.
You can set the alpha value each frame actually, which is very cheap.

The other and more recommended way to solve this is setting up a skin programmatically which has the desired items disabled or enabled (as I mentioned above). Then items will stay the same between animation changes.

Harald escribió

The other and more recommended way to solve this is setting up a skin programmatically which has the desired items disabled or enabled (as I mentioned above). Then items will stay the same between animation changes.

Hi there, would you mind to tell me how to set up a skin programmatically to disable or enable the desired items? I have been studying the skin system for past few days, it looks like the skin can only set up attachments, but I would like to set the slot (because one slot could have more than one attachment). If I can only set up attachments, then how do I make my character’s body part disappeared?

Did you take a look at these forum threads:
Unityでの3.7から追加されたスキンの組み合わせ機能について
How do I represent a lack of attachments in a skin?

And the official documentation pages:
Runtime Skins - Spine Runtimes Guide

If I can only set up attachments, then how do I make my character’s body part disappeared?

When constructing your skin additively from "nothing" as described in the above threads, you can simply not add the attachments to the skin that you want to disappear. Or did I misunderstand you there?

5 días más tarde

Unityでの3.7から追加されたスキンの組み合わせ機能について
How do I represent a lack of attachments in a skin?

And the official documentation pages:
Runtime Skins - Spine Runtimes Guide

Thank you so much for your help!
Under your suggestion, I used skin system and SkeletonRenderSeparator script to achieve the desired outcome without changing any code, and problem solved for the most of my characters. However, the problem still appears on only one of my character, and also this particular character has the problem of an undesired draw order on his face, please check on another post of mine (the link is below).
http://zh.esotericsoftware.com/forum/What-caused-my-character-s-image-hierarchy-went-wrong-11889

The character’s SkeletonData consists of three .png files and three materials. Could this be the reason that caused the character to have these problems? Is it wrong for a SkeletonData to have multiple atlas pages?

Glad that the initial problem could be solved, thanks for letting us know.

I will answer your other posting on the respective thread to keep things in order.

Harald escribió

Glad that the initial problem could be solved, thanks for letting us know.

I will answer your other posting on the respective thread to keep things in order.

Hi Harald, I thought the problem was resolved with the solution above. Unfortunately, it appears that some of my characters still have it, and I really didn’t know what to do with it. Therefore, I have sent you an email to demonstrate my problem (contact@esotericsoftware.com), and I really hope that you could help me with the problem. Thank you so much!

In that email, the attachment is a very simple project of my character. There are two buttons (ButtonA and ButtonB). Firstly, if you click the ButtonA to hide the body part of my character; then, click the ButtonB to check if there is any problem in an array named urrentInstructions.submeshInstructions under SkeletonRenderer, and the result will show that some of the materials are null. I really don't know what cause it, and it is very frustrating for me.


Harald escribió

Glad that the initial problem could be solved, thanks for letting us know.

I will answer your other posting on the respective thread to keep things in order.

SkeletonAnimation skelt = data.spineGB.GetComponent<SkeletonAnimation>();
            Skin newSkin = skelt.Skeleton.GetClonedSkin("CurrentSkin", true);
            Slot[] slots = skelt.skeleton.Slots.Items;
            foreach (Slot item in slots)
            {
                if (item.ToString().Contains("leftHand") == true)
                {
                    if (item.Attachment != null)
                    {
                        newSkin.RemoveAttachment(item.Data.Index, item.Attachment.Name);
                    }
                }
            }

        skelt.Skeleton.Skin = newSkin;
        skelt.Skeleton.Data.DefaultSkin = newSkin;
        skelt.Skeleton.SetSlotsToSetupPose();
        skelt.AnimationState.Apply(skelt.Skeleton);

Above is my code to hide the parts of the character.
I use the role of SkeletonData created a new SkeletonAnimation, but found the new SkeletonAnimation using the Settings above my new skin (hidden hand skin).
May I ask if there is something wrong with the code of my hidden part?
What do you suggest I can in the hidden part of the set separately for each SkeletonAnimation keep it at the same time.

Thank you for the email.

Unfortunately the only included scene SampleScene throws an error upon Play:

"NullReferenceException: Object reference not set to an instance of an object
MyTestScript.Awake () (at Assets/Scripts/MyTestScript.cs:22)"

The line states:

skelt = GameObject.Find("SpineGB").GetComponent<SkeletonAnimation>();

and there is no GameObject named SpineGB in the scene, so this error makes sense. Did you accidentally include the wrong scene?

Harald escribió

Thank you for the email.

Unfortunately the only included scene SampleScene throws an error upon Play:

"NullReferenceException: Object reference not set to an instance of an object
MyTestScript.Awake () (at Assets/Scripts/MyTestScript.cs:22)"

The line states:

skelt = GameObject.Find("SpineGB").GetComponent<SkeletonAnimation>();

and there is no GameObject named SpineGB in the scene, so this error makes sense. Did you accidentally include the wrong scene?

Ummm... That is very strange. I reload the attachment from the email I sent you, and it works perfectly fine in my unity. Anyway, I will send you another email with a compressed file, and please try again. Maybe the file was somehow damaged through compressing by the mail. Thanks!

Oh, there's one more thing I want to ask you about. The code below is how I used the skin system to hide my character's body part. Is there anything wrong with my code? If there is, then where did I do wrong? Do you have any good suggestions?

SkeletonAnimation skelt = data.spineGB.GetComponent<SkeletonAnimation>();
            Skin newSkin = skelt.Skeleton.GetClonedSkin("CurrentSkin", true);
            Slot[] slots = skelt.skeleton.Slots.Items;
            foreach (Slot item in slots)
            {
                if (item.ToString().Contains("leftHand") == true)
                {
                    if (item.Attachment != null)
                    {
                        newSkin.RemoveAttachment(item.Data.Index, item.Attachment.Name);
                    }
                }
            }

        skelt.Skeleton.Skin = newSkin;
        skelt.Skeleton.Data.DefaultSkin = newSkin;
        skelt.Skeleton.SetSlotsToSetupPose();
        skelt.AnimationState.Apply(skelt.Skeleton);

Thanks for the second reproduction package, it now contains everything and shows your described problem.

What you described as "error" is only submesh instructions having no material set (being null) since they are disabled. This is no behaviour to worry about. A similar situation can be created without any custom modifications to the Spine-Unity runtime code, so this is no problem introduced by your code.

Still, I do not understand why
1) you are using a SkeletonRenderSeparator here for every single body part, and
2) modify the Spine-Unity runtime code and access the submesh instructions that are hidden from the user on purpose.

If you only need to modify skin parts, changing attachments as above should be sufficient to hide or switch parts.
If you need other GameObjects objects to follow bones, there is a BoneFollower component for this purpose.

Oh thanks, that explains a lot.Anyway, about my second question, a new problem occurs when I am trying to use the skin system. First, I created a new skin, and removed the attachment which I want to hide .

newSkin.RemoveAttachment(item.Data.Index, item.Attachment.Name);

Then, I set the newskin to the current skin.

SkeletonAnimation.Skeleton.Skin = newSkin;

Since I hide the part of the new skin due to the need of my game mechanics, the character starts to acquire and display the data of the attachment from the Default Skin (which is something I do not want, the whole purpose of it is to make parts of my character disappeared). Therefore, after I created a new skin, I empty the DefaultSkindata .

SkeletonAnimation.Skeleton.Data.DefaultSkin.Clear();

However, when I use the same SkeletonDataAsset to create new character, the new character disappeared because there is no data in the DefaultSkin. So, what should I really do to achieve my desired outcome to hide the body part of my character?

SkeletonAnimation skelt = data.spineGB.GetComponent<SkeletonAnimation>();
            Skin newSkin = skelt.Skeleton.GetClonedSkin("CurrentSkin", true);
            Slot[] slots = skelt.skeleton.Slots.Items;
            foreach (Slot item in slots)
            {
                if (item.ToString().Contains("leftHand") == true)
                {
                    if (item.Attachment != null)
                    {
                        newSkin.RemoveAttachment(item.Data.Index, item.Attachment.Name);
                    }
                }
            }

        skelt.Skeleton.Skin = newSkin;
        skelt.Skeleton.Data.DefaultSkin = newSkin;
        skelt.Skeleton.SetSlotsToSetupPose();
        skelt.AnimationState.Apply(skelt.Skeleton);