原理就是将使用同一套骨骼的模型部分,单独独立起来,制作成可以加载利用的资源。然后加载这些单独的部分,组合成模型。如果要替换某个部分,就将合成的部分资源换成其他就好了。
每个模型部分的骨骼数据要和原来的骨骼数据一致,这样就可以通过一个只有骨骼数据的GameObject,然后加上SkinnedMeshRenderer,用各个部分蒙皮成为一个千变万化的人物了。
因为使用的是骨骼动画,各部分的顶点和骨骼的绑定是预定好的,所有不管部分的变化,动画还是可以正常播放的。
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(SkinnedMeshRenderer))]
public class AvatarPipi : MonoBehaviour {
//事先准备好的部件,美术做好的,可以做成prefab,需要时再通过名字来加载
public List<GameObject> eyes = new List<GameObject>();
public List<GameObject> faces = new List<GameObject>();
public List<GameObject> hairs = new List<GameObject>();
public List<GameObject> pants = new List<GameObject>();
public List<GameObject> shoes = new List<GameObject>();
public List<GameObject> tops = new List<GameObject>();
private List<CombineInstance> mList = new List<CombineInstance>();//子网格信息
private List<Transform> bones = new List<Transform>();//骨骼
private List<Material> mats = new List<Material>();//材质
private List<Transform> allBones = new List<Transform>();
void Start () {
//获取所有的骨骼
allBones.AddRange(transform.GetComponentsInChildren<Transform>());
GeneraterAvatar();
}
void GeneraterAvatar()
{
ChangeMeshType(eyes);
ChangeMeshType(faces);
ChangeMeshType(hairs);
ChangeMeshType(pants);
ChangeMeshType(shoes);
ChangeMeshType(tops);
SkinnedMeshRenderer my = this.GetComponent<SkinnedMeshRenderer>();
//整合所有信息到一个SkinnedMeshRenderer来渲染
Mesh m = new Mesh();
m.CombineMeshes(mList.ToArray(),false,false);//合并子网格
my.sharedMesh = m;
my.materials = mats.ToArray();
my.bones = bones.ToArray();
}
void ChangeMeshType(List<GameObject> type)
{
if (type.Count < 1) return;
GameObject go = type[Random.Range(0, type.Count)];
SkinnedMeshRenderer skin = go.GetComponent<SkinnedMeshRenderer>();
if (skin == null) return;
//获取部件的网格、材质、骨骼,聚合起来到列表中
CombineInstance combine = new CombineInstance();
combine.mesh = skin.sharedMesh;
mList.Add(combine);
mats.AddRange(skin.materials);
GetBones(skin.bones);
}
void GetBones(Transform[] termB)
{
//遍历得到部位组件对应的骨骼
foreach (var bb in termB)
{
foreach (var b in allBones)
{
if (b.name.Equals(bb.name))
{
bones.Add(b);
}
}
}
}
void Update () {
if (Input.GetKeyDown(KeyCode.Space))
{
ResetAvatar();
GeneraterAvatar();
}
}
void ResetAvatar()
{
mList.Clear();
bones.Clear();
mats.Clear();
}
}代码就不分析了,注释都有的。记一下合并网格函数的用法:
Mesh.CombineMeshes(CombineInstance[] combine, bool mergeSubMeshes = true, bool useMatrices = true, bool hasLightmapData = false)
第一个参数就是要合并的网格们,网格的定义在combine.mesh;
第二个参数确定是否和合并网格到一个sub mesh中,不是的话各网格会独立开来,使用不同的材质。如果使用的是合并的材质,那么设置为true。
第三个参数确定是否使用CombineInstance.transform