原理就是将使用同一套骨骼的模型部分,单独独立起来,制作成可以加载利用的资源。然后加载这些单独的部分,组合成模型。如果要替换某个部分,就将合成的部分资源换成其他就好了。
每个模型部分的骨骼数据要和原来的骨骼数据一致,这样就可以通过一个只有骨骼数据的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