换装是将头、身子、腿等多个零部件模型合成一个人物模型。
模型是由网格组成的,所以换装的本质是找到合适的网格进行合并。
而将一些网格,在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();
Dictionary dicbone = new Dictionary();
for (int i = 0; i < allbone.Length; i++)
{
dicbone.Add(allbone[i].name, allbone[i]);
}
//2.根据是否合并子网格判断是否合并材质
List olduvlist = new List();
Material material = new Material(Shader.Find("Custom/Face"));
List materials = new List();
if (mergeSubMeshes)
{
List texture2s = new List();
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 combines = new List();
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 bones = new List();
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().sharedMesh = mesh;
avatar.GetComponent().bones = bones.ToArray();
if (mergeSubMeshes)
{
avatar.GetComponent().material = material;
}
else
{
avatar.GetComponent().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 playerDatas;
int touid;
int yifuid;
int tuiid;
// Start is called before the first frame update
void Start()
{
string json = Resources.Load("player").text;
playerDatas = JsonConvert.DeserializeObject>(json);
for (int i = 0; i < playerDatas.Count; i++)
{
int n = i;
GameObject playerbtn = Instantiate(Resources.Load("playerbtn"), leftContent);
Material material = new Material(Shader.Find("Image"));
material.mainTexture = Resources.Load("image/image_" + playerDatas[n].id);
playerbtn.GetComponent().material = material;
playerbtn.GetComponent
随手写的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
}]
脚本中拖预制体