[Unity实战]详解换装系统(二)

来源:互联网 发布:js div弹出层 编辑:程序博客网 时间:2024/06/01 07:28

首先推荐一些关于换装的好东西:

文章:http://bbs.9ria.com/thread-185477-1-1.html

视频:http://www.unitytrain.cn/course/55

其中代码很大程度上是参考以上两个的。。


首先在场景中我们要有一套带有所有部位的模型,以及一副骨架,前者在场景中时隐藏的,换装的时候,我们就从前者找部位给骨架。

模型的层次:


模型带有所有的部位,如上图所示,有两套face、hair等等,注意一下命名规则。

骨架的层次:


其实就是把模型的Female_Hips取出来了。


模型主要由mesh和骨架组成,因为target已经有骨架了,所以换装时只需要添加或替换mesh,然后将mesh绑定到target的骨架上即可。

代码如下:

using UnityEngine;using System.Collections;using System.Collections.Generic;public class AvatarSys : MonoBehaviour {    public static AvatarSys instance;    //来源模型    private Transform source;    //目标骨架    private Transform target;    //存放来源模型的信息,分别为部位名字,部位编号,部位的SkinnedMeshRenderer组件    //根据部位名字,部位编号取得想要的部位    private Dictionary<string, Dictionary<string, SkinnedMeshRenderer>> sourceData =        new Dictionary<string, Dictionary<string, SkinnedMeshRenderer>>();    //存放目标骨架的信息,分别为部位名字,部位上的SkinnedMeshRenderer组件    //没有编号是因为target上只有一套    private Dictionary<string, SkinnedMeshRenderer> targetSmr =        new Dictionary<string, SkinnedMeshRenderer>();    //存放目标骨架的骨架信息,用于mesh绑定骨架    private Transform[] hips;    //部位信息,目标骨架初始化为模型时需要的部位,注意命名是依据来源模型上的部位命名的    string[,] avatarStr = new string[,]{{"face","1"},{"hair","1"},    {"pants","1"},{"shoes","1"},{"top","1"}};// Use this for initializationvoid Start ()     {        instance = this;        InstantiateSource();//实例化来源模型        InstantiateTarget();//实例化目标骨架        SaveData();//存储来源模型和目标骨架的信息        InitAvatar();//将目标骨架初始化为模型        target.animation.PlayQueued("walk");}    void InstantiateSource()    {        GameObject sourceObj = Instantiate(Resources.Load("Source")) as GameObject;        source = sourceObj.transform;        sourceObj.SetActive(false);    }    void InstantiateTarget()    {        GameObject targetObj = Instantiate(Resources.Load("Target")) as GameObject;        target = targetObj.transform;        hips = target.GetComponentsInChildren<Transform>();    }    void SaveData()    {        if ((source == null) || (target == null))            return;        SkinnedMeshRenderer[] parts = source.GetComponentsInChildren<SkinnedMeshRenderer>(true);//true表示把隐藏部位的组件也获得        foreach (SkinnedMeshRenderer part in parts)        {            string[] partName = part.name.Split('-');            if (!sourceData.ContainsKey(partName[0]))//每有一种新类型的部位            {                //每有一种新类型的部位就在骨架下生成一个空GameObject                GameObject partobj = new GameObject();                partobj.name = partName[0];                partobj.transform.parent = target;                //部位类型只记录一次                sourceData.Add(partName[0], new Dictionary<string, SkinnedMeshRenderer>());                //targetSmr只记录一套部位的信息,例如裤子有两套模型,只记录一套的                //因为target是用来展示的,各种部位只要一套即可                targetSmr.Add(partName[0],partobj.AddComponent<SkinnedMeshRenderer>());            }            //sourceData记录所有部位信息            sourceData[partName[0]].Add(partName[1], part);        }    }    public void ChangeMesh(string part,string num)//传入部位名字,编号    {        //因为sourceData记录所有部位信息,所以可以查找到所要的新的部位        SkinnedMeshRenderer smr = sourceData[part][num];        List<Transform> bones = new List<Transform>();        //根据新的部位绑定的骨架信息,在target的骨架上找到相应的位置        //例如头发mesh,在source中它绑定在脑袋上,那么我们在target上也要找到脑袋这个位置        foreach (Transform bone in smr.bones)        {            foreach(Transform hip in hips)            {                if (hip.name == bone.name)                {                    bones.Add(hip);                    break;                }            }        }        targetSmr[part].materials = smr.materials;//更换材质        targetSmr[part].sharedMesh = smr.sharedMesh;//更换mesh        targetSmr[part].bones = bones.ToArray();//将mesh绑定到合适的骨架中    }    void InitAvatar()    {        int length = avatarStr.GetLength(0);        for (int i = 0; i < length; i++)        {            ChangeMesh(avatarStr[i, 0], avatarStr[i, 1]);        }    }}

要注意的是要将target上的Animation组件的Play Automatically取消掉才能播放动画。

效果图:



模型下载地址:

http://yun.baidu.com/s/1o6FVqCM


0 0