Unity-Spine换装问题

Spine换装问题无非就是更换slot下面的Attachment。
SkeletonAnimation脚本里面给出了更换整套服装的方法,但这不是我们想要的,我们要的是局部换装。比如只换一个帽子或者只换一把武器。
首先第一种,当前使用的图片和将要换上去的图片都没有mesh变形,或者两张图片可以使用同一个mesh。这个时候我们可以直接使用外部图片进行换装。Spine的example里面有个Mix And Match的脚本实现了此功能。我们先来看一下Inspector面板:

效果是这样的

可以看到Spineboy的眼睛和枪被成功替换,下面是核心代码

			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);

			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<string> GetAttachName(string skinName,string slotName)
        {
            ExposedList<Slot> slots = skeletonAnimation.skeleton.slots;
            int index = 0;
            List<string> names = new List<string>();
            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<Slot> 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<Slot> 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<string> 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

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值