角色换装

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


namespace SwordmenWorld
{
    ///
    /// 角色换装处理器
    /// 
    /// 注意:
    /// 1.每个换装的部分都需要有SkinnedMeshRenderer组件
    /// 2.如果使用合并处理,材质上的贴图需要更改未可读写
    ///

    public class CharacterGenerator
    {
        protected Transform root;/*角色根节点*/
        protected bool isCombine = false;/*是否需要合并*/
        protected Dictionary rendsDict;/*缓存角色SkinnedMeshRenderer信息*/
        protected SkinnedMeshRenderer rootRend;
        public CharacterGenerator(Transform root, bool isCombine = false){
            this.root = root;
            this.isCombine = isCombine;
            rendsDict = new Dictionary();
            SkinnedMeshRenderer[] rends = root.GetComponentsInChildren();


            foreach (var item in rends)
            {
                if (!rendsDict.ContainsKey(item.name))
                    rendsDict.Add(item.name, item);
            }


            if (isCombine)
                Combine(root);
        }


        ///
        /// 更换
        ///

        /// 更换节点名称
        /// 更换的物体
        public void Change(string partName, GameObject part)
        {
            SkinnedMeshRenderer rend = part.GetComponent();
            if (rend == null)
            {
                Debug.LogError("Missing Component  ____  SkinnedMeshRenderer");
                return;
            }


            if (!isCombine)
                ChangePart(partName, rend);
            else
                ChangeCombinePart(partName, rend);
        }


        void ChangePart(string partName, SkinnedMeshRenderer rend)
        {
            SkinnedMeshRenderer targetRend = rendsDict[partName];
            if (targetRend != null)
            {
                //rend 使用 targetRend 的骨骼信息
                rend.bones = GetBones(targetRend.bones, rend.bones);
                rend.transform.parent = targetRend.transform.parent;


                rendsDict[partName] = rend;
                GameObject.DestroyImmediate(targetRend.gameObject);
            }
        }


        void ChangeCombinePart(string partName, SkinnedMeshRenderer rend)
        {
            SkinnedMeshRenderer targetRend = rendsDict[partName];
            if (targetRend != null)
            {
                rend.transform.parent = targetRend.transform.parent;
                rendsDict[partName] = rend;
                GameObject.DestroyImmediate(targetRend.gameObject);


                foreach (var item in rendsDict.Values)
                {
                    item.gameObject.SetActive(true);
                }


                //更换部分后,重新合并
                Combine(root);
            }
        }




        void Combine(Transform root)
        {
            float startTime = Time.realtimeSinceStartup;


            if (rootRend == null)
                rootRend = root.gameObject.AddComponent();


            CombineSkinnedMeshRendererInfo info = GetNeedInfo(root);


            rootRend.sharedMesh = GetCombinMesh(info);
            rootRend.bones = info.boneList.ToArray();
            rootRend.material = info.material;


            rootRend.material.mainTexture = GetCombineTexture(info);
            rootRend.sharedMesh.uv = info.atlasUVs;


            Debug.Log("合并耗时:" + (Time.realtimeSinceStartup - startTime) * 1000 + " ms");
        }


        CombineSkinnedMeshRendererInfo GetNeedInfo(Transform root)
        {
            CombineSkinnedMeshRendererInfo info = new CombineSkinnedMeshRendererInfo();
            Transform[] transforms = root.GetComponentsInChildren();
            SkinnedMeshRenderer[] rends = root.GetComponentsInChildren();


            foreach (var item in rends)
            {
                if (item == rootRend) continue;


                if (info.material == null)
                    info.material = GameObject.Instantiate(item.sharedMaterial);


                for (int i = 0; i < item.sharedMesh.subMeshCount; i++)
                {
                    CombineInstance ci = new CombineInstance();
                    ci.mesh = item.sharedMesh;
                    ci.subMeshIndex = i;
                    info.combineInstances.Add(ci);
                }


                info.uvList.Add(item.sharedMesh.uv);
                info.uvCount += item.sharedMesh.uv.Length;


                if (item.material.mainTexture != null)
                {
                    Texture main = item.GetComponent().material.mainTexture;
                    info.textures.Add(main as Texture2D);
                    info.width += main.width;
                    info.height += main.height;
                }


                foreach (Transform bone in item.bones)
                {
                    foreach (Transform trans in transforms)
                    {
                        if (trans.name != bone.name) continue;
                        info.boneList.Add(trans);
                        break;
                    }
                }


                item.gameObject.SetActive(false);
            }


            return info;
        }


        Mesh GetCombinMesh(CombineSkinnedMeshRendererInfo info)
        {
            Mesh mesh = new Mesh();
            mesh.CombineMeshes(info.combineInstances.ToArray(), true, false);
            return mesh;
        }


        Texture2D GetCombineTexture(CombineSkinnedMeshRendererInfo info)
        {
            Texture2D atlas = new Texture2D(Get2Pow(info.width), Get2Pow(info.height));
            Rect[] packingResult = atlas.PackTextures(info.textures.ToArray(), 0);
            info.atlasUVs = new Vector2[info.uvCount];
            int j = 0;
            for (int i = 0; i < info.uvList.Count; i++)
            {
                foreach (Vector2 uv in info.uvList[i])
                {
                    info.atlasUVs[j].x = Mathf.Lerp(packingResult[i].xMin, packingResult[i].xMax, uv.x);
                    info.atlasUVs[j].y = Mathf.Lerp(packingResult[i].yMin, packingResult[i].yMax, uv.y);
                    j++;
                }
            }
            return atlas;
        }


        int Get2Pow(int into)
        {
            int outo = 1;
            for (int i = 0; i < 10; i++)
            {
                outo *= 2;
                if (outo > into)
                {
                    break;
                }
            }


            return outo;
        }


        Transform[] GetBones(Transform[] trans1, Transform[] trans2)
        {
            List bones = new List();


            foreach (Transform item in trans2)
            {
                foreach (Transform bone in trans1)
                {
                    if (bone.name.Equals(item.name))
                    {
                        bones.Add(bone);
                        break;
                    }
                }
            }
            return bones.ToArray();
        }


    }






    public class CombineSkinnedMeshRendererInfo
    {
        public List combineInstances;
        public Material material = null;
        public List boneList;


        public List textures;
        public int width = 0;
        public int height = 0;
        public int uvCount = 0;
        public List uvList;
        public Vector2[] atlasUVs;


        public CombineSkinnedMeshRendererInfo()
        {
            combineInstances = new List();
            boneList = new List();


            textures = new List();
            uvList = new List();
        }
    }
}

你可能感兴趣的:(角色换装)