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

在阅读本文章之前,本人强烈建议你先看看本系列的前两篇文章,对换装系统有一些了解后再继续!


在上一篇文章中,运行之后是这样的:


我们的target上挂上各种类型的mesh,而每一个mesh上都有一个Skinned Mesh Renderer组件,这无疑会增加运算量,根据官方demo的指引,我们应该合并mesh,这样target上就只有一个Skinned Mesh Renderer组件,从而达到优化的目的!


本人对上一篇文章的代码进行了一些修改,主要是添加了6处新的代码,并对不需要的代码进行了注释(不删除),方便了理解。

其实跟上一篇文章的代码差别不太大。上一篇文章是针对target下的单个部位更换mesh,绑定骨架。而这片文章是先把target需要的所有部件的mesh、材质、骨架全部存放好,再一次性的赋给target下唯一的Skinned Mesh Renderer。


代码如下:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class AvatarSys2 : MonoBehaviour{

    public static AvatarSys2 instance;

    //来源模型
    private Transform source;
    //目标骨架
    private Transform target;

    //存放来源模型的信息,分别为部位名字,部位编号,部位的SkinnedMeshRenderer组件
    //根据部位名字,部位编号取得想要的部位
    private Dictionary<string, Dictionary<string, SkinnedMeshRenderer>> sourceData =
        new Dictionary<string, Dictionary<string, SkinnedMeshRenderer>>();

    //new 01
    SkinnedMeshRenderer targetSmr;//target上只有一个SkinnedMeshRenderer组件
    List<CombineInstance> combineInstances = new List<CombineInstance>();//存放target上的所有mesh
    List<Material> materials = new List<Material>();//存放target上的所有material
    List<Transform> bones = new List<Transform>();//存放target上的所有bone

    存放目标骨架的信息,分别为部位名字,部位上的SkinnedMeshRenderer组件
    没有编号是因为target上只有一套
    //private Dictionary<string, SkinnedMeshRenderer> targetSmr =
    //    new Dictionary<string, SkinnedMeshRenderer>();

    //存放目标骨架的骨架信息,用于mesh绑定骨架
    private Transform[] hips;

    //部位信息,目标骨架初始化为模型时需要的部位,注意命名是依据来源模型上的部位命名的
    public string[,] avatarStr = new string[,]{{"face","1"},{"hair","1"},
    {"pants","1"},{"shoes","1"},{"top","1"}};

    // Use this for initialization
    void 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>();
        //new 02
        targetSmr = target.gameObject.AddComponent<SkinnedMeshRenderer>();
    }

    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绑定到合适的骨架中

        //new 03
        CombineInstance ci = new CombineInstance();
        ci.mesh = smr.sharedMesh;
        combineInstances.Add(ci);
        materials.AddRange(smr.materials);
    }

    //new 04
    public void InitAvatar()
    {
        Empty();
        int length = avatarStr.GetLength(0);
        for (int i = 0; i < length; i++)
        {
            ChangeMesh(avatarStr[i, 0], avatarStr[i, 1]);
        }
        Combine();
    }

    //new 05
    void Empty()
    {
        combineInstances.Clear();
        materials.Clear();
        bones.Clear();
    }

    //new 06
    void Combine()
    {
        targetSmr.sharedMesh = new Mesh();
        targetSmr.sharedMesh.CombineMeshes(combineInstances.ToArray(),false,false);
        targetSmr.materials = materials.ToArray();
        targetSmr.bones = bones.ToArray();
    }
}

运行后:





怎么样?很酷吧!

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值