换装的核心思想:更换对象SkinnerMeshRenderer组件中骨骼(Bones),网格(sharedMesh),材质(materials)。麻烦的是要找到对应的部位,对应的SkinnerMeshRenderer组件,对应的位置。以下,介绍一种比较简单的换装过程。
通常要实现换装需要有一个带有“所有需要更换部件”的模型,如下:(图1)中FemaleModel为模型,其中Female_Hips为骨骼信息。(图2)为场景用的样子,可以看出它是很多重复部件的叠加物。
接下来说说具体的思路;
1.先制作骨骼模型
2.将原模型和骨骼模型实例化出来
3.建立字典存储所有SkinnerMeshRenderer信息
4.建立字典存储所有骨骼信息
5.将相应的信息存储在字典中
6.以名字进行一一对应,更换相应SkinnerMeshRenderer中三大件
接下来是每步详细步骤:
1.先制作骨骼模型
其实这部很简单,只需要将原模型中所有部件删除,只留下骨骼部分,如图:其中FemaleTarget就是骨骼模型
2.将原模型和骨骼模型实例化出来
//原模型
GameObject avatarModel;
//骨骼模型
GameObject avatarTarget;
//所有骨骼信息
Transform[] Hips;
void InitSys() {
//实例化对应的模型并将其隐藏
avatarModel = Instantiate(Resources.Load("FemaleModel") as GameObject); avatarModel.SetActive(false);
//实例化要显示的对象并将其所有(骨骼)Transform信息收集
avatarTarget = Instantiate(Resources.Load("FemaleTarget") as GameObject);
Hips = avatarTarget.GetComponentsInChildren();
}
3.建立字典存储所有SkinnerMeshRenderer信息
//存储模型的所有信息 //建立字典存储模型的SkinnerMeshRenderer信息
Dictionary> avaStr = new Dictionary>();
4.建立字典存储所有骨骼信息
//建立字典存储要显示对象的skinnerMeshRenderer信息
Dictionary avaSmr = new Dictionary();
5.将相应的信息存储在字典中
void SaveAvatarData() {
SkinnedMeshRenderer[] skis = avatarModel.GetComponentsInChildren();
foreach (SkinnedMeshRenderer ski in skis) {
string[] names = ski.name.Split('-');
//如果字典中没有存储这个部位
if (!avaStr.ContainsKey(names[0])) {
//那么就新建一个对象当做这个对象
GameObject part = new GameObject();
part.name = names[0];
//set父级为要显示的任务骨骼
part.transform.parent = avatarTarget.transform;
//并且给它添加ski组件并加入字典中
avaSmr.Add(names[0], part.AddComponent());
//存入模型字典并实例化相应子字典
avaStr.Add(names[0], new Dictionary());
}
//将序号和SkinnerMeshRenderer信息存入模型字典
avaStr[names[0]].Add(names[1], ski); } }
6.以名字进行一一对应,更换相应SkinnerMeshRenderer中三大件
//换装
//参数:换装部位名称,序号
void ChangeMesh(string name,string num) {
//从模型字典中拿到相应的SkinnerMeshRenderer
SkinnedMeshRenderer skm = avaStr[name][num];
//创建集合来存储(骨骼)Transform信息
List<Transform> bones = new List<Transform>();
foreach (var trans in skm.bones)
{
foreach (var bone in Hips)
{
if (bone.name == trans.name)
{
bones.Add(trans);
break;
}
}
}
avaSmr[name].bones = bones.ToArray();
avaSmr[name].materials = skm.materials;
avaSmr[name].sharedMesh = skm.sharedMesh;
}
至此,基本的换装的过程就此基本完成,接下来就是让要显示的模型有一个初始状态
7.先建立字典存储模型最初的状态:
//显示初始状态
//建立数组
string[,] avatar = new string[,] { {"face","1" }, { "top", "1" }, { "pants", "1" }, { "hair", "1" },
{ "eyes", "1" }, { "shoes", "1" } };
8.依次调用换装方法(注意此处遍历的是数组的行数而不是数组长度)
void InitAvatar()
{
for (int i = 0; i < avatar.GetLength(0); i++)
{
ChangeMesh(avatar[i, 0], avatar[i, 1]);
}
}
好了,现在试下调用如上方法就可以让模型显示出来啦。
void Start () {
InitSys();
SaveAvatarData();
InitAvatar();
}
进行换装就是调用ChangeMesh方法就好。如下举个小栗子~
void Update () {
print(ani.isPlaying);
if (Input.GetKeyDown(KeyCode.Space))
{
ChangeMesh("top", "2");
}
}
Ps:这里还有一个问题就是模型的动画没有播放出来(虽然ani.isPlaying结果是true)。我怀疑是我中途换了Unity版本导致。就不深究啦~~
如上如果有任何不严谨或是错误的地方,欢迎各位指正~~嘻嘻~~
不忘初心的Tiny