人物换装-Avatar

原理就是将使用同一套骨骼的模型部分,单独独立起来,制作成可以加载利用的资源。然后加载这些单独的部分,组合成模型。如果要替换某个部分,就将合成的部分资源换成其他就好了。

每个模型部分的骨骼数据要和原来的骨骼数据一致,这样就可以通过一个只有骨骼数据的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


首页 我的博客
粤ICP备17103704号