直奔主题:
但愿进来的朋友们可以帮助到你们。老规矩,话不多说,直接上代码:
方法1:
也是我后来用的方法,可能不一定是最好的,最适合你的,发出来供大家学习参考。
这也是很重要的一步,需要提前和美术同学制定好命名规则,换装的时候需要按照这个命名去给指定部位换装:
//和美术约定好的Slot(插槽)名字
const string face_slot_name = "s_face";
const string hair_slot_name = "s_hair";
此处只定义了两个Slot(插槽),也就是当前Spine只支持这两个部位的动态换装:
//定义目标Slot(插槽),并赋值。
Slot face_slot;
Slot hair_slot;
获取目标Slot:
//初始化Slot相关
private void init_slot_info()
{
//通过slot name获取指定的slot
//当然前提得保证美术在制作Spine的时候预留了这些Slot
face_slot = this.GetComponent<SkeletonGraphic>().Skeleton.FindSlot(face_slot_name);
hair_slot = this.GetComponent<SkeletonGraphic>().Skeleton.FindSlot(hair_slot_name);
}
定义一个集合来储存所有可换装的Attachment(附件):
//所有皮肤下的所有附件
//string 皮肤名
//string 插槽名
//List<Attachment>> 对应皮肤下对应插槽的Attachment
Dictionary<string ,Dictionary<string , Attachment>> att_dic;
最后大概写一下换装流程:
/// <summary>
/// 局部换装
/// </summary>
/// <param name="skin_name">目标皮肤名</param>
/// <param name="part_name">目标部位名</param>
public void change_part(string skin_name ,string part_name)
{
var tar_att = att_dic[skin_name][part_name];
var tar_slot = get_slot(part_name);
tar_slot.Attachment = tar_att;
}
/// <summary>
/// 只写了大概,没写容错等其他内容
/// </summary>
/// <param name="slot_name">目标部位名</param>
/// <returns></returns>
private Slot get_slot(string slot_name)
{
switch (slot_name)
{
case face_slot_name:
return face_slot;
case hair_slot_name:
return hair_slot;
default:
return null;
}
}
//初始化Slot相关
private void init_slot_info()
{
//通过slot name获取指定的slot
//当然前提得保证美术在制作Spine的时候预留了这些Slot
face_slot = this.GetComponent<SkeletonGraphic>().Skeleton.FindSlot(face_slot_name);
hair_slot = this.GetComponent<SkeletonGraphic>().Skeleton.FindSlot(hair_slot_name);
}
这个方法最后实际用的时候是可行的,就是拿到目标Slot,给他目标Attachment。但是参考网上的其他实现方式的话,这样好像显得有些粗暴,下面是第二种,也是我在网上看到的别人的实现方式。
方法2:
大概原理是,new一个空的皮肤,然后拿到目标Attachment。然后遍历当前Spine的所有Slot,如果当前Slot是目标Slot,则把目标Attachment赋值给Slot(Skin.SkinEntry entry = new Skin.SkinEntry(slot_index, slot_name, temp_att)),如果不是目标Slot,则获取当前Slot上的Attachment。最后把当前new出来的皮肤赋值给Spine。
代码:
/// <summary>
/// 局部换装
/// </summary>
/// <param name="skin_name">目标皮肤名</param>
/// <param name="part_name">目标部位名</param>
public void change_part(string skin_name, string part_name)
{
//先拿到目标Attachment
var tar_att = att_dic[skin_name][part_name];
Skin new_skin = new Skin("new_skin");
var skeleton = this.GetComponent<SkeletonGraphic>().Skeleton;
var slots = skeleton.Slots;
foreach (var slot in slots)
{
string slot_name = slot.Data.Name;
int slot_index = slot.Data.Index;
Attachment temp_att = null;
if (slot_name.Equals(part_name))
{
temp_att = tar_att;
}
else
{
temp_att = slot.Attachment;
}
Skin.SkinEntry entry = new Skin.SkinEntry(slot_index, slot_name, temp_att);
new_skin.Attachments.Add(entry, temp_att);
}
skeleton.SetSkin(new_skin);
}
写在最后:
有一点一定要注意,就是在写代码之前,要和美术同学提前沟通并制定好命名规则,然后按这个命名去换装。别的暂时想不到了,欢迎提出问题一起学习。
20220519更新
需要在替换过attachment之后,执行下面两个方法:
//skeleton.SetSkin(newSkin); // 1. Set the active skin
skeleton.SetSlotsToSetupPose(); // 2. Use setup pose to set base attachments.
animationState.Apply(skeleton); // 3. Use AnimationState to set attachments active in the current movement.