nyxpixel

A little note, I previously used Maya for my 2D animation. In order to allow my characters to swap weapons, I would leave the plane that contained their weapon texture separate from the mesh I would generate for all the other body parts. This allowed me to swap weapons and character look independently (obviously at the cost of an extra draw call, but this was not a concern).

I am trying to achieve something similar with Spine now, and it would seem this Mix and Match approach is as close as I can get, and is the Spine supported method. I am just having trouble getting it to work.

What I have done is go in to my existing Spine project, created a skin called template, and added a skin placeholder where the weapon image was previously.There was no keying done on the slot at all, only the bone it was attached to. I exported this to Unity.
spine_ss.PNG


I then made a new sprite asset of the weapon used by this unit, colored differently (I used the image from the spine project as a template), and imported to Unity.

Finally, I have taken the mix and match script and attempted to customize it to my needs. Then attached it to my Spine gameobject in the Unity scene.



kobold_mix_and_match.PNG

using UnityEngine;
using Spine.Unity.Modules.AttachmentTools;
using System.Collections;

namespace Spine.Unity.Examples {

// This is an example script that shows you how to change images on your skeleton using UnityEngine.Sprites.
public class MixAndMatch : MonoBehaviour {

#region Inspector
[SpineSkin]
public string templateAttachmentsSkin = "base";
public Material sourceMaterial; // This will be used as the basis for shader and material property settings.

[Header("weapon")]
public Sprite visorSprite;
[SpineSlot] public string weapon;
[SpineAttachment(slotField: "weapon", skinField: "baseSkinName")] public string weaponKey = "weapon";

/*[Header("Gun")]
public Sprite gunSprite;
[SpineSlot] public string gunSlot;
[SpineAttachment(slotField:"gunSlot", skinField:"baseSkinName")] public string gunKey = "gun";*/


[Header("Runtime Repack")]
public bool repack = true;
public BoundingBoxFollower bbFollower;

[Header("Do not assign")]
public Texture2D runtimeAtlas;
public Material runtimeMaterial;
#endregion
public bool applyPMA = true;

Skin customSkin;

void OnValidate () {
if (sourceMaterial == null) {
var skeletonAnimation = GetComponent<SkeletonAnimation>();
if (skeletonAnimation != null)
sourceMaterial = skeletonAnimation.SkeletonDataAsset.atlasAssets[0].materials[0];
}
}

IEnumerator Start () {
yield return new WaitForSeconds(1f); // Delay for one second before applying. For testing.
Apply();
}

void Apply () {
Debug.Log("ran mandm");
var skeletonAnimation = GetComponent<SkeletonAnimation>();
var skeleton = skeletonAnimation.Skeleton;

// STEP 0: PREPARE SKINS
// Let's prepare a new skin to be our custom skin with equips/customizations. We get a clone so our original skins are unaffected.
customSkin = customSkin ?? new Skin("custom skin"); // This requires that all customizations are done with skin placeholders defined in Spine.
//customSkin = customSkin ?? skeleton.UnshareSkin(true, false, skeletonAnimation.AnimationState); // use this if you are not customizing on the default skin.
var templateSkin = skeleton.Data.FindSkin(templateAttachmentsSkin);

// STEP 1: "EQUIP" ITEMS USING SPRITES
// STEP 1.1 Find the original/template attachment.
// Step 1.2 Get a clone of the original/template attachment.
// Step 1.3 Apply the Sprite image to the clone.
// Step 1.4 Add the remapped clone to the new custom skin.

// Let's do this for the visor.
int weaponSlotIndex = skeleton.FindSlotIndex(weapon); // You can access GetAttachment and SetAttachment via string, but caching the slotIndex is faster.
Debug.Log(weaponSlotIndex);
Attachment templateAttachment = templateSkin.GetAttachment(weaponSlotIndex, weapon); // STEP 1.1
Debug.Log(templateAttachment.Name);
Attachment newAttachment = templateAttachment.GetRemappedClone(visorSprite, sourceMaterial, premultiplyAlpha: this.applyPMA); // STEP 1.2 - 1.3
Debug.Log(newAttachment.Name);
customSkin.SetAttachment(weaponSlotIndex, weapon, newAttachment); // STEP 1.4

if (repack) {
var repackedSkin = new Skin("repacked skin");
repackedSkin.Append(skeleton.Data.DefaultSkin); // Include the "default" skin. (everything outside of skin placeholders)
repackedSkin.Append(customSkin); // Include your new custom skin.
repackedSkin = repackedSkin.GetRepackedSkin("repacked skin", sourceMaterial, out runtimeMaterial, out runtimeAtlas); // Pack all the items in the skin.
skeleton.SetSkin(repackedSkin); // Assign the repacked skin to your Skeleton.
if (bbFollower != null) bbFollower.Initialize(true);
} else {
skeleton.SetSkin(customSkin); // Just use the custom skin directly.
}

skeleton.SetSlotsToSetupPose(); // Use the pose from setup pose.
skeletonAnimation.Update(0); // Use the pose in the currently active animation.

Resources.UnloadUnusedAssets();
}
}
}
I am not a professional coder, just trying my best to get this method to replace my current workflow, and it's proving difficult. Any assistance would be greatly appreciated!
nyxpixel
Posts: 3

Pharan

Hi! Thanks for the details. Looks like you didn't change anything that would have ended up breaking it.
But you didn't actually mention what went wrong.

Anyway, to narrow down what might have problems, can you try both cases? with Repack enabled, and Repack disabled.
And please do mention any warnings or errors that appeared or missing behavior that you observed. Otherwise, we can't really tell.
User avatar
Pharan

Pharan
Posts: 5009

nyxpixel

Thanks for the reply. Good point, sorry I forgot to provide some details about what I tried already. So I tried a number of things, but it boiled down to this:

If I had the 'default' skin selected, instead of the template as shown above, I didn't receive any error. The scene would launch, the enemies original weapon sprite would disappear, but the new sprite would not show. Repacking seemed to work properly here, when checked I see the repacked skin with the new weapon sprite added. When unchecked it, still no errors, same result of sprite weapon not showing.

When I have the template skin selected, the scene errors out with a null reference exception: object not set to an instance of an object. If I remove my debug.logs, it occurs at the line:
customSkin.SetAttachment(weaponSlotIndex, weapon, newAttachment);
Which seems to occur because newAttachment is null. Image below of Unity Console
kobold_unity_console_error_1.PNG


In an effort to see where in the chain things were actually breaking, I added the debugs you see in the previous post. With those in, it errors out at:
Debug.Log(templateAttachment.Name);
kobold_unity_console_error_2.PNG



Obviously in the second case, repack being checked or uncheck currently doesn't affect anything since it doesn't reach that code.

Thanks again for your help, very much appreciated!
nyxpixel
Posts: 3

Pharan

Since Debug.Log(templateAttachment.Name) threw a NullReferenceException, templateAttachment was probably not found.

I think I found your error.

Skin.GetAttachment's signature is:
Skin.GetAttachment(slotIndex, keyName)

But what you did is
Skin.GetAttachment(slotIndex, slotName)

The second argument needs to the be key name (weaponKey). Not the slot name. (weapon).

skins-editor-and-runtime.png


So in your case, it should read:
Attachment templateAttachment = templateSkin.GetAttachment(weaponSlotIndex, weaponKey);
---

I'm currently in the process of finishing up some improved documentation for skins and attachments.
I think if you give it a quick read some time, you'll be a bit more confident about how the system works.
At the very least, the illustrations might be helpful.
https://github.com/pharan/spine-unity-docs/blob/master/Attachments-and-AttachmentTools.md

Though if not, do let me know.
User avatar
Pharan

Pharan
Posts: 5009

nyxpixel

That definitely was the problem. Thanks for finding that! I also had this line wrong
customSkin.SetAttachment(weaponSlotIndex, weapon, newAttachment);
Once I changed that to 'weaponKey', everything started working.

I will read through that new documentation and see if I can't get a better hang of this thing, thanks for providing it, looks like great stuff.
nyxpixel
Posts: 3

Pharan

I would say it's not really too necessary go read through the whole doc. But if you ever get lost, it's there to help you make sense of things.
User avatar
Pharan

Pharan
Posts: 5009


Return to Unity