【转】-Unity3D SkinnedMeshRenderer换装系统
转载请注明出处:http://www.cnblogs.com/shamoyuu/p/6505561.html
一、换装原理
游戏角色换装分为以下几步:
1.替换蒙皮网格
2.刷新骨骼
3.替换材质
上面这种是比较简单的换装,可以实现,但是一般我们为了降低游戏的Draw Call会合并模型的网格,这就需要我们重新计算UV,还要合并贴图和材质。这种复杂的实现分为以下几步:
1.替换蒙皮网格(或者直接替换模型换装部位的GameObject,因为合并的时候会合并所有的蒙皮网格,而不会关心它是否属于原来角色身体的一部分,而且如果需要替换的部位有多个配件拥有独立的网格和贴图,那这种方式都可以正常执行。我下面的代码就是直接替换了换装部位的GameObject)
2.合并所有蒙皮网格
3.刷新骨骼
4.附加材质(我下面是获取第一个材质作为默认材质)
5.合并贴图(贴图的宽高最好是2的N次方的值)
6.重新计算UV
二、换装实现
1 using UnityEngine;
2 using System.Collections.Generic;
3
4
5 public class CharacterCombine : MonoBehaviour
6 {
7 // 目标物体(角色根物体)
8 public GameObject target;
9
10 // 需要替换的部位(这里以body做示例)
11 public GameObject targetBody;
12
13 // 用来替换上面部位的物体(这是一个独立的fbx文件,它里面可以包含多个子物体和贴图,但是必须有骨骼及蒙皮)
14 public GameObject bodyTest;
15
16
17 void Start()
18 {
19 //CombineTest();
20 }
21
22
23 public void CombineTest()
24 {
25 Debug.Log("模型合并开始");
26
27 // 为角色增加换装物体
28 GameObject go = Instantiate(bodyTest);
29 go.transform.parent = target.transform;
30
31 // 禁用角色原来的body,这样就不会计算它的网格
32 targetBody.SetActive(false);
33
34 // 合并蒙皮网格和贴图,刷新骨骼
35 Combine(target.transform);
36
37 Destroy(go);
38 }
39
40
41 /// <summary>
42 /// 合并蒙皮网格,刷新骨骼
43 /// 注意:合并后的网格会使用同一个Material
44 /// </summary>
45 /// <param name="root">角色根物体</param>
46 public void Combine(Transform root)
47 {
48 float startTime = Time.realtimeSinceStartup;
49
50 List<CombineInstance> combineInstances = new List<CombineInstance>();
51 Material material = null;
52 List<Transform> boneList = new List<Transform>();
53 Transform[] transforms = root.GetComponentsInChildren<Transform>();
54 List<Texture2D> textures = new List<Texture2D>();
55
56 int width = 0;
57 int height = 0;
58
59 int uvCount = 0;
60
61 List<Vector2[]> uvList = new List<Vector2[]>();
62
63 // 遍历所有蒙皮网格渲染器,以计算出所有需要合并的网格、UV、骨骼的信息
64 foreach (SkinnedMeshRenderer smr in root.GetComponentsInChildren<SkinnedMeshRenderer>())
65 {
66 if (material == null)
67 {
68 material = Instantiate(smr.sharedMaterial);
69 }
70
71 for (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++)
72 {
73 CombineInstance ci = new CombineInstance();
74 ci.mesh = smr.sharedMesh;
75 ci.subMeshIndex = sub;
76 combineInstances.Add(ci);
77 }
78
79 uvList.Add(smr.sharedMesh.uv);
80 uvCount += smr.sharedMesh.uv.Length;
81
82 if (smr.material.mainTexture != null)
83 {
84 textures.Add(smr.GetComponent<Renderer>().material.mainTexture as Texture2D);
85 width += smr.GetComponent<Renderer>().material.mainTexture.width;
86 height += smr.GetComponent<Renderer>().material.mainTexture.height;
87 }
88
89 foreach (Transform bone in smr.bones)
90 {
91 foreach (Transform item in transforms)
92 {
93 if (item.name != bone.name) continue;
94 boneList.Add(item);
95 break;
96 }
97 }
98
99 smr.gameObject.SetActive(false);
100 }
101
102 // 获取并配置角色所有的SkinnedMeshRenderer
103 SkinnedMeshRenderer tempRenderer = root.gameObject.GetComponent<SkinnedMeshRenderer>();
104 if (!tempRenderer)
105 {
