zoukankan      html  css  js  c++  java
  • Unity换装效果

    Unity换装效果


    前言:在我们游戏的角色中,我们点击按钮可以为自己的角色改变外观。然而我们建好的3D模型,如果要将其中的一个部位换成另一个形状。最直接的就是将该物件部位的Mesh替换掉。那么我们的外观改变了,但是这种方法如果运用到需要动作的模型上,将会发现被置换的部位不会正常的工作。所以直接改变Mesh的方法之适用于静态模型物件。为此,我们必须找到更好的方法。

    一:换装的原理:                                                    

    首先我们需要了解模型的结构:当我们把人物的预设拖到场景中,在Hierarchy视窗将物件展开来,会发现几个名称相同并使用数字来区别的物件。它们分别代表着人物的模型。所以,整个人物的模型当中包含着多个相同部位的模型。而Female_Bip01则是整个人物的骨架结构。人物的动作则是设置在FemaleAvatar的Aniamtion,所以这个模型只是一个模型资源,而不是实际上要放到场景中的目标物件。

    原理:说白了就是我们拿到原始的模型,然后再按照需求将各个部位重新组合成一个新的目标模型。首先我们需要原模型和目标骨架。我们设置为私有(进行动态的加载)我们将进行一步步的编码。

        private Transform _source;                      //模型资源的位置
        private Transform _target;                      //目标骨架的位置

    我们声明一个方法进行动态的加载,当我们把原模型在Hierarchy面板中声明出来后就可以进行隐藏了,因为我们需要进行模型的重组。把位置信息进行赋值。

        /// <summary>
        /// 动态加载原模型资源
        /// </summary>
        private void LoadSourceModel()
        {
            GameObject femaleAvatar = Instantiate(Resources.Load("FemaleAvatar") as GameObject);
            GameObject targetModel = Instantiate(Resources.Load("targetmodel") as GameObject);
            if (femaleAvatar != null)
            {
                _source = femaleAvatar.transform;
                femaleAvatar.SetActive(false);
            }
            if (targetModel != null)
            {
                _target = targetModel.transform;
            }
        }

    这时候我们需要想两个问题,我们把原模型中的各个部位的信息存放在哪里呢?我们重组的之后模型的信息放在哪里呢?这时候我们需要声明两个集合来存储各个部位的信息。

        /** 
         * 目标位置各部件的 SkinnedMeshRenderer
         * */
        private readonly Dictionary<string, SkinnedMeshRenderer> _targetSkinnedMeshRenderers = new Dictionary<string, SkinnedMeshRenderer>();
        /**
         *  模型资源资料 Key:string:对应部位  Value: string:对应编号  Transform:具体的皮肤位置
         * */
        private readonly Dictionary<string, Dictionary<string, Transform>> _datas = new Dictionary<string, Dictionary<string, Transform>>();

    我们把目标骨架的位置也复制一份存放在数组中:

        private Transform[] _hips;                      //目标物件的骨架
        void Start()
        {
            //从目标物体取得骨架资料
            _hips = _target.GetComponentsInChildren<Transform>();
        }

    接下来需要得到模型皮肤的各种信息,并且存储到集合中。

        /// <summary>
        /// 得到模型皮肤并且存储到集合中
        /// </summary>
        /// <param name="source"></param>
        private void GetModelSkinnedMeshRenderer(Transform source)
        {
            //参数的检查
            if (source == null) return;
            //取出原模型资源的各部位的SkinnedMeshRenderer并且放在数组中进行存储
            SkinnedMeshRenderer[] skinnedMeshRenderers = source.GetComponentsInChildren<SkinnedMeshRenderer>(true);
            foreach (SkinnedMeshRenderer skinnedMeshRenderer in skinnedMeshRenderers)
            {
                //利用string.Split('')进行字符串的分割
                string[] partName = skinnedMeshRenderer.name.Split('-');
                //存储在集合中
                //如果当前的集合中没有这个键时
                if (!_datas.ContainsKey(partName[0]))
                {
                    //添加部位的名称
                    _datas.Add(partName[0], new Dictionary<string, Transform>());
                    //创建新的GameObject并使用部位名称来命名,指定为目标物件的子物体
                    GameObject partObj = new GameObject();
                    //新建的物体进行重新的命名
                    partObj.name = partName[0];
                    //设置父节点
                    partObj.transform.parent = _target;
                    //为新建立的GameObejct加入SkinnedMeshRenderer 并放入到集合中
                    //我们新创建的各个部位的节点并添加皮肤组件,但是这时候并没有进行赋值
                    _targetSkinnedMeshRenderers.Add(partName[0], partObj.AddComponent<SkinnedMeshRenderer>());
                }
                //如果存在键的话进行值的添加
                _datas[partName[0]].Add(partName[1], skinnedMeshRenderer.transform);
            }
        }

    进行换装的方法,首先我们需要得到要替换的皮肤的信息,我们需要取得相对用名称的骨架列表来建立新的骨架列表。不能直接把骨骼赋值过去。我们需要一个集合去存储当前要替换的皮肤的骨架列表。然后进行指定部位的更新——1.更新网格  2.更新骨架  3.更新材质。

        /// <summary>
        /// 进行换装
        /// </summary>
        /// <param name="part">要改变的部位</param>
        /// <param name="item"></param>
        private void ChangePart(string part, string item)
        {
            //取得当前需要替换的皮肤
            SkinnedMeshRenderer skinned = _datas[part][item].GetComponent<SkinnedMeshRenderer>();
    
            //取得相对应名称的骨架物件来建立新的骨架列表 不能直接把骨骼赋值过去
            List<Transform> bones = new List<Transform>();
            //bones:用于蒙皮网格的骨骼列表
            foreach (Transform bone in skinned.bones)
            {
                foreach (Transform hip in _hips)
                {
                    if (hip.name != bone.name) continue;
                    bones.Add(hip);
                    break;
                }
            }
    
            //更新指定的部位
    
            //更新网格
            _targetSkinnedMeshRenderers[part].sharedMesh = skinned.sharedMesh;
            //更新骨架
            _targetSkinnedMeshRenderers[part].bones = bones.ToArray();
            //更新材质
            _targetSkinnedMeshRenderers[part].materials = skinned.materials;
        }

     

    初始化皮肤:

     

        /// <summary>
        /// 模型的重组(初始化皮肤)
        /// </summary>
        private void StartRestructuringModel()
        {
            foreach (KeyValuePair<string, Dictionary<string, Transform>> data in _datas)
            {
                switch (data.Key)
                {
                    case "coat":
                        ChangePart("coat", "001");
                        break;
                    case "foot":
                        ChangePart("foot", "001");
                        break;
                    case "hair":
                        ChangePart("hair", "001");
                        break;
                    case "hand":
                        ChangePart("hand", "001");
                        break;
                    case "pant":
                        ChangePart("pant", "001");
                        break;
                    case "head":
                        ChangePart("head", "001");
                        break;
                    default:
                        break;
                }
            }
        }

    我们也可以写一个方法实现的单个物件的换装:

     

        /// <summary>
        /// 单件衣服的换装
        /// </summary>
        private void CoatChange()
        {
            if (Count == 1)
            {
                ChangePart("coat", "003");
                Count = 0;
            }
            else if (Count == 0)
            {
                ChangePart("coat", "001");
                Count = 1;
            }
        }

     


    最终效果如下图:

     百度网盘地址(项目工程源码和脚本流程分析图):http://pan.baidu.com/s/1kUHiIqV

  • 相关阅读:
    解决ios手机页面overflow scroll滑动很卡的问题
    响应式网页设计:rem、em设置网页字体大小自适应
    linq 和lambda查询
    单元测试的学习
    各种仓储模式的学习
    ref 微软官网
    泛型 学习
    aop 和castle 的一些 学习文章
    autofac 的好博文
    json 序列化和反序列化的3个方法
  • 原文地址:https://www.cnblogs.com/MoRanQianXiao/p/7883298.html
Copyright © 2011-2022 走看看