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.
![](https://i.imgur.com/9nNd0xW.png)
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.List
1 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
}