shawnblais

  • 24 de Ago de 2018
  • Se unió 17 de Ago de 2018
  • Ahh, that docs page was definitely the missing link I needed to explain the current system, and your script also works perfectly. Thanks!

  • Hey guys, having a very frustrating time trying to get a very simple implementation working, where I swap 1 sprite for another in Unity 2018.1.9f1. We have a generic in-hand attachment in spine, and would like to swap it with any number of sprites in Unity, all with the same origin pt, and texture size.

    I've tried 3 different methods, and encountered 3 totally different bugs. To make it worse, many links on your forums and faqs just 404 to github (ie: https://github.com/pharan/spine-unity-docs/blob/master/Mix-and-Match.md)

    BoneFollower
    I tried using a bone follower, attached to the slot, which almost works... but when flipping the X, there's a a 1-frame flicker where the rotation is inverted: http://treefortress.com/shared/2018-08-24_10-58-42.mp4,

    I tried changing from LateUpdate to Update, but there are dependancies littered through the code-base calling into this class with LateUpdate, so it turned into a can of worms. Tried changing script execution order, didn't work.

    SpriteAttacher
    I tried using SpriteAttacher, but it rendered a white sprite for no discernible reason.

    If I select "PMA Vertex Colors" on the Skeleton, it then decides to work... But it took me a long time to notice that, and I'm still confused as to why?

    AddAttachment Method
    I then tried the method shown in EquipMixAndMatch example, and despite doing everything properly, the editor is throwing errors.

    NullReferenceException: Object reference not set to an instance of an object
    Spine.Unity.MeshGenerator.GenerateSkeletonRendererInstruction (Spine.Unity.SkeletonRendererInstruction instructionOutput, Spine.Skeleton skeleton, System.Collections.Generic.Dictionary2 customSlotMaterials, System.Collections.Generic.List1 separatorSlots, Boolean generateMeshOverride, Boolean immutableTriangles) (at Assets/Plugins/Spine/spine-unity/Mesh Generation/SpineMesh.cs:366)
    Spine.Unity.SkeletonRenderer.LateUpdate () (at Assets/Plugins/Spine/spine-unity/Components/SkeletonRenderer.cs:274)

    The weird thing about the error above, if I step through it in the debugger... nothing is null. So confused...

    UPDATE: If I set PMA to true, in the templateAttachment.GetRemappedClone() call, Method 3 works, and the error goes away. So it seems I need to use the ToRegionAttachmentPMAClone() API regardless if I want PMA or not, just to get a valid attachment, something is broken with sprite.ToAtlasRegion()

    Overall I find it very hard to diagnose what is going wrong at any step in the process, there's so many dependancies, extension scripts, editor utilities, and so much complexity, and all I want to do, is just attach 1 sprite to 1 bone, and it's been a massive headache.

    It's great that the advanced use cases are served, but it would be nice to add like a single, self-contained code-based example, of attaching a unity sprite, to a bone in the most straightforward way with as little architecture in the way to obfuscate the actual API calls.


    In the interest of being constructive, here's the bare minimum script I could come up with, that works currently in Unity to swap a sprite:

    using UnityEngine;
    
    using Spine.Unity.Modules.AttachmentTools;
    using Spine.Unity;
    using Spine;
    
    public class SpineSlotAttacher : MonoBehaviour
    {
        public SkeletonAnimation skeletonAnimation;
        [SpineSkin]
        public string skinName;
        [SpineSlot]
        public string targetSlot;
        [SpineAttachment(currentSkinOnly = true)]
        public string attachmentName;
        //Sprite to swap
        public Sprite sprite;
    
    //Internal
    int slotIndex;
    Material sourceMaterial;
    Attachment templateAttachment;
    Skin defaultSkin;
    Skin equipsSkin = new Skin("Equips");
    Material runtimeMaterial;
    Texture2D runtimeAtlas;
    
    void Start() {
        var dataAsset = skeletonAnimation.SkeletonDataAsset;
        var skeletonData = dataAsset.GetSkeletonData(true);
        sourceMaterial = dataAsset.atlasAssets[0].materials[0];
        defaultSkin = skeletonData.FindSkin(skinName);
        slotIndex = skeletonData.FindSlotIndex(targetSlot);
        templateAttachment = defaultSkin.GetAttachment(slotIndex, attachmentName);
        //Append the baseSkin to our new skin, so we don't lose any existing attachments.
        equipsSkin.Append(defaultSkin);
        //Attach unity sprite 
        AttachSprite(sprite);
    }
    
    public void AttachSprite(Sprite sprite, bool optimize = true) {
       //PMA must be true here, or null reference errors will be thrown
        var attachment = templateAttachment.GetRemappedClone(sprite, sourceMaterial, premultiplyAlpha: true);
        equipsSkin.AddAttachment(slotIndex, attachmentName, attachment);
        if (optimize) {
            skeletonAnimation.Skeleton.Skin = equipsSkin.GetRepackedSkin("Repacked skin", skeletonAnimation.SkeletonDataAsset.atlasAssets[0].materials[0], out runtimeMaterial, out runtimeAtlas);
        } else {
            skeletonAnimation.Skeleton.Skin = equipsSkin;
        }
        RefreshSkeletonAttachments();
    }
    
    void RefreshSkeletonAttachments() {
        skeletonAnimation.Skeleton.SetSlotsToSetupPose();
        skeletonAnimation.AnimationState.Apply(skeletonAnimation.Skeleton); //skeletonAnimation.Update(0);
    }
    }
    

    With the script above, it now becomes trivial to swap-sprites from some other control script:

    void Update() {
            if (Input.GetKeyDown(KeyCode.Alpha1)) {
                slotAttacher.AttachSprite(axe);
            }
            if (Input.GetKeyDown(KeyCode.Alpha2)) {
                slotAttacher.AttachSprite(shovel);
            }
    //ETC
        }