换装是将头、身子、腿等多个零部件模型合成一个人物模型。
模型是由网格组成的,所以换装的本质是找到合适的网格进行合并。
而将一些网格,在cpu上转换他们的顶点,将许多相似的顶点组合在一起,并一次性绘制他们,的这个过程我们也称之为——动态合批。
一般物由MeshFilter 网格过滤器和MeshRenderer 网格渲染器两个组件来对模型进行渲染,为了进行网格的动态合批,这里我们先认识一个新的类,合并所需要的数据结构CombineInstance。
换装资源准备
1.每一套装备模型必须使用同一套骨骼,并单独将骨骼数据保存成一个Prefab,骨骼数据在Unity中的展示形式就是Transform。
2.将模型拆分成多个部分,将每一个部分单独保存成Prefab,Prefab中只保留需要的部位模型和骨骼,武器也单独保存为一个Prefab。
3.每一个Prefab都含有自身的SkinnedMeshRenderer。
合并网格逻辑代码:
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.根据是否合并子网格判断是否合并材质
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.取出网格合并
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调用控制代码
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
}]
脚本中拖预制体
网友评论