[Unity實戰]詳解換裝系統(三)
阿新 • • 發佈:2019-01-31
在閱讀本文章之前,本人強烈建議你先看看本系列的前兩篇文章,對換裝系統有一些瞭解後再繼續!
在上一篇文章中,執行之後是這樣的:
我們的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(); } }
執行後:
怎麼樣?很酷吧!