換裝是將頭、身子、腿等多個零部件模型合成一個人物模型。
模型是由網(wǎng)格組成的,所以換裝的本質(zhì)是找到合適的網(wǎng)格進行合并。
而將一些網(wǎng)格,在cpu上轉(zhuǎn)換他們的頂點,將許多相似的頂點組合在一起,并一次性繪制他們,的這個過程我們也稱之為——動態(tài)合批。
一般物由MeshFilter 網(wǎng)格過濾器和MeshRenderer 網(wǎng)格渲染器兩個組件來對模型進行渲染,為了進行網(wǎng)格的動態(tài)合批,這里我們先認識一個新的類,合并所需要的數(shù)據(jù)結(jié)構(gòu)CombineInstance。

換裝資源準備

1.每一套裝備模型必須使用同一套骨骼,并單獨將骨骼數(shù)據(jù)保存成一個Prefab,骨骼數(shù)據(jù)在Unity中的展示形式就是Transform。

2.將模型拆分成多個部分,將每一個部分單獨保存成Prefab,Prefab中只保留需要的部位模型和骨骼,武器也單獨保存為一個Prefab。

3.每一個Prefab都含有自身的SkinnedMeshRenderer。

合并網(wǎng)格邏輯代碼:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CombineMesh
{
public static void Combine(GameObject avatar,SkinnedMeshRenderer[] skinneds,bool mergeSubMeshes)
{
//1.取出所有骨骼
Transform[] allbone = avatar.GetComponentsInChildren<Transform>();
Dictionary<string, Transform> dicbone = new Dictionary<string, Transform>();
for (int i = 0; i < allbone.Length; i++)
{
dicbone.Add(allbone[i].name, allbone[i]);
}
//2.根據(jù)是否合并子網(wǎng)格判斷是否合并材質(zhì)
List<Vector2[]> olduvlist = new List<Vector2[]>();
Material material = new Material(Shader.Find("Custom/Face"));
List<Material> materials = new List<Material>();
if (mergeSubMeshes)
{
List<Texture2D> texture2s = new List<Texture2D>();
for (int i = 0; i < skinneds.Length; i++)
{
texture2s.Add(skinneds[i].sharedMaterial.GetTexture("_BackTex") as Texture2D);
}
Texture2D texture = new Texture2D(1024, 1024);
Rect[] rects = texture.PackTextures(texture2s.ToArray(), 0);
//修改模型uv坐標
for (int i = 0; i < skinneds.Length; i++)
{
Vector2[] olduv = skinneds[i].sharedMesh.uv;
olduvlist.Add(olduv);
Vector2[] newuv = new Vector2[olduv.Length];
for (int j = 0; j < olduv.Length; j++)
{
float uvx = rects[i].x + rects[i].width * olduv[j].x;
float uvy = rects[i].y + rects[i].height * olduv[j].y;
newuv[j] = new Vector2(uvx, uvy);
}
skinneds[i].sharedMesh.uv = newuv;
}
Texture2D face = skinneds[0].sharedMaterial.GetTexture("_MainTex") as Texture2D;
material.SetTexture("_BackTex", texture);
material.SetTexture("_MainTex", face);
material.SetFloat("_PosX", texture.width / face.width);
material.SetFloat("_PosY", texture.height / face.height);
}
else
{
for (int i = 0; i < skinneds.Length; i++)
{
materials.Add(skinneds[i].sharedMaterial);
}
}
//3.取出網(wǎng)格合并
List<CombineInstance> combines = new List<CombineInstance>();
for (int i = 0; i < skinneds.Length; i++)
{
CombineInstance combine = new CombineInstance();
combine.mesh = skinneds[i].sharedMesh;
combine.transform = skinneds[i].transform.localToWorldMatrix;
combines.Add(combine);
}
Mesh mesh = new Mesh();
mesh.CombineMeshes(combines.ToArray(), mergeSubMeshes, false);
//4.找到需要使用的骨骼
List<Transform> bones = new List<Transform>();
for (int i = 0; i < skinneds.Length; i++)
{
for (int j = 0; j < skinneds[i].bones.Length; j++)
{
if (dicbone.ContainsKey(skinneds[i].bones[j].name))
{
bones.Add(dicbone[skinneds[i].bones[j].name]);
}
}
}
//5.賦值
avatar.GetComponent<SkinnedMeshRenderer>().sharedMesh = mesh;
avatar.GetComponent<SkinnedMeshRenderer>().bones = bones.ToArray();
if (mergeSubMeshes)
{
avatar.GetComponent<SkinnedMeshRenderer>().material = material;
}
else
{
avatar.GetComponent<SkinnedMeshRenderer>().materials = materials.ToArray();
}
}
}
效果

簡單ui調(diào)用控制代碼
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Newtonsoft.Json;
public class PlayerData
{
public int id;
public int career;
public int tou;
public int yifu;
public int tui;
}
public class CombinePlayer : MonoBehaviour
{
public GameObject[] tou;
public GameObject[] yifu;
public GameObject[] tui;
public GameObject[] left;
public GameObject[] right;
public GameObject[] maozi;
public GameObject avatar;
public Transform maozibone;
public Transform leftbone;
public Transform rightbone;
public Transform leftContent;
public Transform rightContent;
public Image[] zhuangbei;
List<PlayerData> playerDatas;
int touid;
int yifuid;
int tuiid;
// Start is called before the first frame update
void Start()
{
string json = Resources.Load<TextAsset>("player").text;
playerDatas = JsonConvert.DeserializeObject<List<PlayerData>>(json);
for (int i = 0; i < playerDatas.Count; i++)
{
int n = i;
GameObject playerbtn = Instantiate(Resources.Load<GameObject>("playerbtn"), leftContent);
Material material = new Material(Shader.Find("Image"));
material.mainTexture = Resources.Load<Texture>("image/image_" + playerDatas[n].id);
playerbtn.GetComponent<Image>().material = material;
playerbtn.GetComponent<Button>().onClick.AddListener(() =>
{
touid = playerDatas[n].tou;
yifuid = playerDatas[n].yifu;
tuiid = playerDatas[n].tui;
combine();
Material material0 = new Material(Shader.Find("Image"));
material0.mainTexture = Resources.Load<Texture>("image/tou_" + playerDatas[n].tou);
zhuangbei[0].material = material0;
Material material1 = new Material(Shader.Find("Image"));
material1.mainTexture = Resources.Load<Texture>("image/yifu_" + playerDatas[n].yifu);
zhuangbei[1].material = material1;
Material material2 = new Material(Shader.Find("Image"));
material2.mainTexture = Resources.Load<Texture>("image/tui_" + playerDatas[n].tui);
zhuangbei[2].material = material2;
});
}
for (int i = 0; i < 3; i++)
{
int n = i;
GameObject playerbtn = Instantiate(Resources.Load<GameObject>("playerbtn"), rightContent);
Material material = new Material(Shader.Find("Image"));
material.mainTexture = Resources.Load<Texture>("image/tou_" + n);
playerbtn.GetComponent<Image>().material = material;
playerbtn.GetComponent<Button>().onClick.AddListener(() =>
{
touid = n;
combine();
zhuangbei[0].material = material;
});
}
for (int i = 0; i < 3; i++)
{
int n = i;
GameObject playerbtn = Instantiate(Resources.Load<GameObject>("playerbtn"), rightContent);
Material material = new Material(Shader.Find("Image"));
material.mainTexture = Resources.Load<Texture>("image/yifu_" + n);
playerbtn.GetComponent<Image>().material = material;
playerbtn.GetComponent<Button>().onClick.AddListener(() =>
{
yifuid = n;
combine();
zhuangbei[1].material = material;
});
}
for (int i = 0; i < 3; i++)
{
int n = i;
GameObject playerbtn = Instantiate(Resources.Load<GameObject>("playerbtn"), rightContent);
Material material = new Material(Shader.Find("Image"));
material.mainTexture = Resources.Load<Texture>("image/tui_" + n);
playerbtn.GetComponent<Image>().material = material;
playerbtn.GetComponent<Button>().onClick.AddListener(() =>
{
tuiid = n;
combine();
zhuangbei[2].material = material;
});
}
for (int i = 0; i < 3; i++)
{
int n = i;
GameObject playerbtn = Instantiate(Resources.Load<GameObject>("playerbtn"), rightContent);
Material material = new Material(Shader.Find("Image"));
material.mainTexture = Resources.Load<Texture>("image/prop_" + n);
playerbtn.GetComponent<Image>().material = material;
playerbtn.GetComponent<Button>().onClick.AddListener(() =>
{
if (leftbone.childCount > 0)
{
Destroy(leftbone.GetChild(0).gameObject);
}
Instantiate(left[n], leftbone);
zhuangbei[4].material = material;
});
}
for (int i = 0; i < 3; i++)
{
int n = i;
GameObject playerbtn = Instantiate(Resources.Load<GameObject>("playerbtn"), rightContent);
Material material = new Material(Shader.Find("Image"));
material.mainTexture = Resources.Load<Texture>("image/prop_" + n);
playerbtn.GetComponent<Image>().material = material;
playerbtn.GetComponent<Button>().onClick.AddListener(() =>
{
if (rightbone.childCount > 0)
{
Destroy(rightbone.GetChild(0).gameObject);
}
Instantiate(right[n], rightbone);
zhuangbei[5].material = material;
});
}
}
public void combine()
{
List<SkinnedMeshRenderer> skinneds = new List<SkinnedMeshRenderer>();
skinneds.Add(tou[touid].GetComponentInChildren<SkinnedMeshRenderer>());
skinneds.Add(yifu[yifuid].GetComponentInChildren<SkinnedMeshRenderer>());
skinneds.Add(tui[tuiid].GetComponentInChildren<SkinnedMeshRenderer>());
CombineMesh.Combine(avatar, skinneds.ToArray(), false);
}
// Update is called once per frame
void Update()
{
}
}
隨手寫的json
[{
"id":0,
"career":0,
"tou":0,
"yifu":0,
"tui":0
},{
"id":1,
"career":1,
"tou":1,
"yifu":1,
"tui":1
},{
"id":2,
"career":2,
"tou":2,
"yifu":2,
"tui":2
}]
腳本中拖預(yù)制體
