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

### Spine 换装系统实现 Spine 是一款强大的骨骼动画制作软件,广泛应用于游戏开发中的角色动画。对于2D角色换装功能,在Spine中可以通过调整骨架结构以及利用皮肤(Skin)特性来达成。 在Spine编辑器内部创建不同风格或者类型的服装作为独立的部件,这些部件可以被分配到不同的皮肤下[^1]。当导入至Unity引擎并使用`SkeletonAnimation`组件时,能够通过编程方式切换指定的角色外观。具体来说: - **准备阶段**:设计好基础模型之后,为每一个可更换部位单独绘制素材,并将其设置成可以在Spine里自由组合的形式。 - **配置皮肤**:将各个服饰选项按照逻辑分组保存为多个皮肤实例。这允许开发者轻松管理多种装扮方案而不必重复构建整个角色架构。 - **运行时期操作**:借助于C#脚本控制当前显示哪一套衣服。例如,下面这段代码展示了如何动态改变玩家所穿装备的方法之一[^2]: ```csharp using UnityEngine; using Spine.Unity; public class ChangeOutfit : MonoBehaviour { public SkeletonAnimation skeletonAnimation; //绑定的对象 void Start(){ SetNewSkin("skinName"); //启动时调用特定皮肤名称方法 } public void SetNewSkin(string skinName){ skeletonAnimation.skeleton.SetSkin(skinName); skeletonAnimation.skeleton.SetSlotsToSetupPose(); //重置slot回到默认姿态 } } ``` 上述过程实现了基本的换肤机制,而更复杂的交互需求则可能涉及到更多细节处理,比如混合过渡效果或是根据条件自动适配某些配件等高级玩法。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值