Spine换装问题无非就是更换slot下面的Attachment。
SkeletonAnimation脚本里面给出了更换整套服装的方法,但这不是我们想要的,我们要的是局部换装。比如只换一个帽子或者只换一把武器。
首先第一种,当前使用的图片和将要换上去的图片都没有mesh变形,或者两张图片可以使用同一个mesh。这个时候我们可以直接使用外部图片进行换装。Spine的example里面有个Mix And Match的脚本实现了此功能。我们先来看一下Inspector面板:
效果是这样的
可以看到Spineboy的眼睛和枪被成功替换,下面是核心代码
var skeletonAnimation = GetComponent();
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);
int visorSlotIndex = skeleton.FindSlotIndex(visorSlot); // You can access GetAttachment and SetAttachment via string, but caching the slotIndex is faster.
Attachment templateAttachment = templateSkin.GetAttachment(visorSlotIndex, visorKey); // STEP 1.1
Attachment newAttachment = templateAttachment.GetRemappedClone(visorSprite, sourceMaterial); // STEP 1.2 - 1.3
customSkin.SetAttachment(visorSlotIndex, visorKey, newAttachment); // STEP 1.4
// And now for the gun.
int gunSlotIndex = skeleton.FindSlotIndex(gunSlot);
Attachment templateGun = templateSkin.GetAttachment(gunSlotIndex, gunKey); // STEP 1.1
Attachment newGun = templateGun.GetRemappedClone(gunSprite, sourceMaterial); // STEP 1.2 - 1.3
if (newGun != null) customSkin.SetAttachment(gunSlotIndex, gunKey, newGun); // 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.
从代码可以看出,直接替换当前slot下面的attachment然后生成一套新皮肤,在用setskin方法替换整个皮肤就完成了换装。
那么第二种呢,如果两张图的mesh不一样,也就是说是spine里面的两套皮肤要做局部换装应该这么做。思路还是差不多的,也就是更换attachment。因为不能上图的关系,我直接放代码讲思路了:
定义一个SkinName的枚举,用来保存所有皮肤的名字
public enum SkinName
{
1,
2,
3
}
定义一个EquipmentAttach的结构体,记录要换的skinname和slot
[System.Serializable]
public struct EquipmentAttach
{
public SkinName skinName;
[SpineSlot(dataField: EquipmentBase.EQUIP_SKELETON_DATA)]
public string slotName;
}
首先要获取到slot下面的attachmentName,当然也可以手动选择,但是如果attachment太多的话就很烦
public List GetAttachName(string skinName,string slotName)
{
ExposedList slots = skeletonAnimation.skeleton.slots;
int index = 0;
List names = new List();
for (int i = 0; i < slots.Count; i++)
{
Slot tempSlot = slots.Items[i];
if (tempSlot.data.name == slotName)
{
index = i;
}
}
skeletonAnimation.skeleton.data.FindSkin(skinName).FindNamesForSlot(index, names);
return names;
}
然后根据attachmentname得到具体的attachment
public Attachment GetAttachment(int slotIndex, string attachmentName, string skinName)
{
var targetSkin = skeletonAnimation.skeleton.data.FindSkin(skinName);
var attachments = targetSkin.Attachments;
Attachment attachment;
attachments.TryGetValue(new Skin.AttachmentKeyTuple(slotIndex, attachmentName), out attachment);
return attachment;
}
有了attachment之后就可以直接setskin了
public void SetSkin(Skin dynamicSkin, string slotName, string targetSkinName,string attachName)
{
Slot changeSlot = skeletonAnimation.skeleton.FindSlot(slotName);
if (dynamicSkin != null)
{
var targetSkin = skeletonAnimation.skeleton.data.FindSkin(targetSkinName);
ExposedList slots = skeletonAnimation.skeleton.slots;
for (int i = 0; i < slots.Count; i++)
{
Slot tempSlot = slots.Items[i];
if (tempSlot.data.name == changeSlot.data.name)
{
Attachment targetAttachment;
targetAttachment = GetAttachment(i, attachName, targetSkinName);
changeSlot.attachment = targetAttachment;
dynamicSkin.Attachments[new Skin.AttachmentKeyTuple(i, attachName)] = targetAttachment;
tempSlot = changeSlot;
}
}
skeletonAnimation.skeleton.slots = slots;
}
skeletonAnimation.skeleton.skin = dynamicSkin;
}
这样的话换装就完成了,换装之后当然还要取下来,取下来就比较简单了,直接把刚刚的attachemnt设置为null,再把原来的换上去就行了
public void ResetSkin(Skin dynamicSkin, string slotName, string targetSkinName, string attachName)
{
Slot changeSlot = skeletonAnimation.skeleton.FindSlot(slotName);
if (dynamicSkin != null)
{
var targetSkin = skeletonAnimation.skeleton.data.FindSkin(targetSkinName);
ExposedList slots = skeletonAnimation.skeleton.slots;
for (int i = 0; i < slots.Count; i++)
{
Slot tempSlot = slots.Items[i];
if (tempSlot.data.name == changeSlot.data.name)
{
dynamicSkin.Attachments[new Skin.AttachmentKeyTuple(i, attachName)] = null;
tempSlot = changeSlot;
}
}
skeletonAnimation.skeleton.slots = slots;
}
skeletonAnimation.skeleton.skin = dynamicSkin;
}
具体的换装调用函数
public void UpgradeEquipmentAttach(EquipmentAttach[] attach)
{
for (int i = 0; i < attach.Length; i++)
{
List attachName = GetAttachName(attach[i].skinName.ToString(), attach[i].slotName);
for (int j = 0; j < attachName.Count; j++)
{
SetSkin(skeletonAnimation.skeleton.data.FindSkin(defaultSkinName.ToString()), attach[i].slotName, attach[i].skinName.ToString(), attachName[j]);
}
}
}
有问题的话欢迎大家指出问题,谢谢。QQ联系412484723