using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace SwordmenWorld
{
/// <summary>
/// 角色换装处理器
///
/// 注意:
/// 1.每个换装的部分都需要有SkinnedMeshRenderer组件
/// 2.如果使用合并处理,材质上的贴图需要更改未可读写
/// </summary>
public class CharacterGenerator
{
protected Transform root;/*角色根节点*/
protected bool isCombine = false;/*是否需要合并*/
protected Dictionary<string, SkinnedMeshRenderer> rendsDict;/*缓存角色SkinnedMeshRenderer信息*/
protected SkinnedMeshRenderer rootRend;
public CharacterGenerator(Transform root, bool isCombine = false){
this.root = root;
this.isCombine = isCombine;
rendsDict = new Dictionary<string, SkinnedMeshRenderer>();
SkinnedMeshRenderer[] rends = root.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (var item in rends)
{
if (!rendsDict.ContainsKey(item.name))
rendsDict.Add(item.name, item);
}
if (isCombine)
Combine(root);
}
/// <summary>
/// 更换
/// </summary>
/// <param name="partName"></param> 更换节点名称
/// <param name="part"></param> 更换的物体
public void Change(string partName, GameObject part)
{
SkinnedMeshRenderer rend = part.GetComponent<SkinnedMeshRenderer>();
if (rend == null)
{
Debug.LogError("Missing Component ____ SkinnedMeshRenderer");
return;
}
if (!isCombine)
ChangePart(partName, rend);
else
ChangeCombinePart(partName, rend);
}
void ChangePart(string partName, SkinnedMeshRenderer rend)
{
SkinnedMeshRenderer targetRend = rendsDict[partName];
if (targetRend != null)
{
//rend 使用 targetRend 的骨骼信息
rend.bones = GetBones(targetRend.bones, rend.bones);
rend.transform.parent = targetRend.transform.parent;
rendsDict[partName] = rend;
GameObject.DestroyImmediate(targetRend.gameObject);
}
}
void ChangeCombinePart(string partName, SkinnedMeshRenderer rend)
{
SkinnedMeshRenderer targetRend = rendsDict[partName];
if (targetRend != null)
{
rend.transform.parent = targetRend.transform.parent;
rendsDict[partName] = rend;
GameObject.DestroyImmediate(targetRend.gameObject);
foreach (var item in rendsDict.Values)
{
item.gameObject.SetActive(true);
}
//更换部分后,重新合并
Combine(root);
}
}
void Combine(Transform root)
{
float startTime = Time.realtimeSinceStartup;
if (rootRend == null)
rootRend = root.gameObject.AddComponent<SkinnedMeshRenderer>();
CombineSkinnedMeshRendererInfo info = GetNeedInfo(root);
rootRend.sharedMesh = GetCombinMesh(info);
rootRend.bones = info.boneList.ToArray();
rootRend.material = info.material;
rootRend.material.mainTexture = GetCombineTexture(info);
rootRend.sharedMesh.uv = info.atlasUVs;
Debug.Log("合并耗时:" + (Time.realtimeSinceStartup - startTime) * 1000 + " ms");
}
CombineSkinnedMeshRendererInfo GetNeedInfo(Transform root)
{
CombineSkinnedMeshRendererInfo info = new CombineSkinnedMeshRendererInfo();
Transform[] transforms = root.GetComponentsInChildren<Transform>();
SkinnedMeshRenderer[] rends = root.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (var item in rends)
{
if (item == rootRend) continue;
if (info.material == null)
info.material = GameObject.Instantiate(item.sharedMaterial);
for (int i = 0; i < item.sharedMesh.subMeshCount; i++)
{
CombineInstance ci = new CombineInstance();
ci.mesh = item.sharedMesh;
ci.subMeshIndex = i;
info.combineInstances.Add(ci);
}
info.uvList.Add(item.sharedMesh.uv);
info.uvCount += item.sharedMesh.uv.Length;
if (item.material.mainTexture != null)
{
Texture main = item.GetComponent<Renderer>().material.mainTexture;
info.textures.Add(main as Texture2D);
info.width += main.width;
info.height += main.height;
}
foreach (Transform bone in item.bones)
{
foreach (Transform trans in transforms)
{
if (trans.name != bone.name) continue;
info.boneList.Add(trans);
break;
}
}
item.gameObject.SetActive(false);
}
return info;
}
Mesh GetCombinMesh(CombineSkinnedMeshRendererInfo info)
{
Mesh mesh = new Mesh();
mesh.CombineMeshes(info.combineInstances.ToArray(), true, false);
return mesh;
}
Texture2D GetCombineTexture(CombineSkinnedMeshRendererInfo info)
{
Texture2D atlas = new Texture2D(Get2Pow(info.width), Get2Pow(info.height));
Rect[] packingResult = atlas.PackTextures(info.textures.ToArray(), 0);
info.atlasUVs = new Vector2[info.uvCount];
int j = 0;
for (int i = 0; i < info.uvList.Count; i++)
{
foreach (Vector2 uv in info.uvList[i])
{
info.atlasUVs[j].x = Mathf.Lerp(packingResult[i].xMin, packingResult[i].xMax, uv.x);
info.atlasUVs[j].y = Mathf.Lerp(packingResult[i].yMin, packingResult[i].yMax, uv.y);
j++;
}
}
return atlas;
}
int Get2Pow(int into)
{
int outo = 1;
for (int i = 0; i < 10; i++)
{
outo *= 2;
if (outo > into)
{
break;
}
}
return outo;
}
Transform[] GetBones(Transform[] trans1, Transform[] trans2)
{
List<Transform> bones = new List<Transform>();
foreach (Transform item in trans2)
{
foreach (Transform bone in trans1)
{
if (bone.name.Equals(item.name))
{
bones.Add(bone);
break;
}
}
}
return bones.ToArray();
}
}
public class CombineSkinnedMeshRendererInfo
{
public List<CombineInstance> combineInstances;
public Material material = null;
public List<Transform> boneList;
public List<Texture2D> textures;
public int width = 0;
public int height = 0;
public int uvCount = 0;
public List<Vector2[]> uvList;
public Vector2[] atlasUVs;
public CombineSkinnedMeshRendererInfo()
{
combineInstances = new List<CombineInstance>();
boneList = new List<Transform>();
textures = new List<Texture2D>();
uvList = new List<Vector2[]>();
}
}
}
using System.Collections.Generic;
using UnityEngine;
namespace SwordmenWorld
{
/// <summary>
/// 角色换装处理器
///
/// 注意:
/// 1.每个换装的部分都需要有SkinnedMeshRenderer组件
/// 2.如果使用合并处理,材质上的贴图需要更改未可读写
/// </summary>
public class CharacterGenerator
{
protected Transform root;/*角色根节点*/
protected bool isCombine = false;/*是否需要合并*/
protected Dictionary<string, SkinnedMeshRenderer> rendsDict;/*缓存角色SkinnedMeshRenderer信息*/
protected SkinnedMeshRenderer rootRend;
public CharacterGenerator(Transform root, bool isCombine = false){
this.root = root;
this.isCombine = isCombine;
rendsDict = new Dictionary<string, SkinnedMeshRenderer>();
SkinnedMeshRenderer[] rends = root.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (var item in rends)
{
if (!rendsDict.ContainsKey(item.name))
rendsDict.Add(item.name, item);
}
if (isCombine)
Combine(root);
}
/// <summary>
/// 更换
/// </summary>
/// <param name="partName"></param> 更换节点名称
/// <param name="part"></param> 更换的物体
public void Change(string partName, GameObject part)
{
SkinnedMeshRenderer rend = part.GetComponent<SkinnedMeshRenderer>();
if (rend == null)
{
Debug.LogError("Missing Component ____ SkinnedMeshRenderer");
return;
}
if (!isCombine)
ChangePart(partName, rend);
else
ChangeCombinePart(partName, rend);
}
void ChangePart(string partName, SkinnedMeshRenderer rend)
{
SkinnedMeshRenderer targetRend = rendsDict[partName];
if (targetRend != null)
{
//rend 使用 targetRend 的骨骼信息
rend.bones = GetBones(targetRend.bones, rend.bones);
rend.transform.parent = targetRend.transform.parent;
rendsDict[partName] = rend;
GameObject.DestroyImmediate(targetRend.gameObject);
}
}
void ChangeCombinePart(string partName, SkinnedMeshRenderer rend)
{
SkinnedMeshRenderer targetRend = rendsDict[partName];
if (targetRend != null)
{
rend.transform.parent = targetRend.transform.parent;
rendsDict[partName] = rend;
GameObject.DestroyImmediate(targetRend.gameObject);
foreach (var item in rendsDict.Values)
{
item.gameObject.SetActive(true);
}
//更换部分后,重新合并
Combine(root);
}
}
void Combine(Transform root)
{
float startTime = Time.realtimeSinceStartup;
if (rootRend == null)
rootRend = root.gameObject.AddComponent<SkinnedMeshRenderer>();
CombineSkinnedMeshRendererInfo info = GetNeedInfo(root);
rootRend.sharedMesh = GetCombinMesh(info);
rootRend.bones = info.boneList.ToArray();
rootRend.material = info.material;
rootRend.material.mainTexture = GetCombineTexture(info);
rootRend.sharedMesh.uv = info.atlasUVs;
Debug.Log("合并耗时:" + (Time.realtimeSinceStartup - startTime) * 1000 + " ms");
}
CombineSkinnedMeshRendererInfo GetNeedInfo(Transform root)
{
CombineSkinnedMeshRendererInfo info = new CombineSkinnedMeshRendererInfo();
Transform[] transforms = root.GetComponentsInChildren<Transform>();
SkinnedMeshRenderer[] rends = root.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (var item in rends)
{
if (item == rootRend) continue;
if (info.material == null)
info.material = GameObject.Instantiate(item.sharedMaterial);
for (int i = 0; i < item.sharedMesh.subMeshCount; i++)
{
CombineInstance ci = new CombineInstance();
ci.mesh = item.sharedMesh;
ci.subMeshIndex = i;
info.combineInstances.Add(ci);
}
info.uvList.Add(item.sharedMesh.uv);
info.uvCount += item.sharedMesh.uv.Length;
if (item.material.mainTexture != null)
{
Texture main = item.GetComponent<Renderer>().material.mainTexture;
info.textures.Add(main as Texture2D);
info.width += main.width;
info.height += main.height;
}
foreach (Transform bone in item.bones)
{
foreach (Transform trans in transforms)
{
if (trans.name != bone.name) continue;
info.boneList.Add(trans);
break;
}
}
item.gameObject.SetActive(false);
}
return info;
}
Mesh GetCombinMesh(CombineSkinnedMeshRendererInfo info)
{
Mesh mesh = new Mesh();
mesh.CombineMeshes(info.combineInstances.ToArray(), true, false);
return mesh;
}
Texture2D GetCombineTexture(CombineSkinnedMeshRendererInfo info)
{
Texture2D atlas = new Texture2D(Get2Pow(info.width), Get2Pow(info.height));
Rect[] packingResult = atlas.PackTextures(info.textures.ToArray(), 0);
info.atlasUVs = new Vector2[info.uvCount];
int j = 0;
for (int i = 0; i < info.uvList.Count; i++)
{
foreach (Vector2 uv in info.uvList[i])
{
info.atlasUVs[j].x = Mathf.Lerp(packingResult[i].xMin, packingResult[i].xMax, uv.x);
info.atlasUVs[j].y = Mathf.Lerp(packingResult[i].yMin, packingResult[i].yMax, uv.y);
j++;
}
}
return atlas;
}
int Get2Pow(int into)
{
int outo = 1;
for (int i = 0; i < 10; i++)
{
outo *= 2;
if (outo > into)
{
break;
}
}
return outo;
}
Transform[] GetBones(Transform[] trans1, Transform[] trans2)
{
List<Transform> bones = new List<Transform>();
foreach (Transform item in trans2)
{
foreach (Transform bone in trans1)
{
if (bone.name.Equals(item.name))
{
bones.Add(bone);
break;
}
}
}
return bones.ToArray();
}
}
public class CombineSkinnedMeshRendererInfo
{
public List<CombineInstance> combineInstances;
public Material material = null;
public List<Transform> boneList;
public List<Texture2D> textures;
public int width = 0;
public int height = 0;
public int uvCount = 0;
public List<Vector2[]> uvList;
public Vector2[] atlasUVs;
public CombineSkinnedMeshRendererInfo()
{
combineInstances = new List<CombineInstance>();
boneList = new List<Transform>();
textures = new List<Texture2D>();
uvList = new List<Vector2[]>();
}
}
}