首先你要会2D轮转图。
通过2D轮转图、蒙皮网格渲染器和合并骨骼实现换装。
首先是2D轮转图与蒙皮脚本的关联,下面代码中的changeAvator.Change()方法。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class MyCyclogram2D : MonoBehaviour, IDragHandler, IEndDragHandler
{
List<GameObject> m_List = new List<GameObject>();
List<Transform> m_Trans = new List<Transform>();
public GameObject m_Prefab;//图片预制体
int m_Num;//图片数量
int indexHead = 11;
int indexLeg = 5;
int indexClothes = 4;
float m_Rad;//弧度
string partName = "";//部位名称
public float m_Spacing;
float m_Radius;//半径
float m_MoveRad;//移动弧度
public ChangeAvator changeAvator;
private void Start()
{
//截取部位信息
string objName = transform.name;
partName = objName.Split('-')[1];
if (partName == "Head")
{
m_Num = indexHead;
}
else if (partName == "Leg")
{
m_Num = indexLeg;
}
else if (partName == "Clothes")
{
m_Num = indexClothes;
}
m_Rad = 2 * Mathf.PI / m_Num;
m_Radius = (m_Prefab.GetComponent<RectTransform>().rect.width + m_Spacing) * m_Num / (2 * Mathf.PI);
OnMove();
}
private void OnMove()
{
for (int i = 0; i < m_Num; i++)
{
float x = Mathf.Sin(i * m_Rad + m_MoveRad) * m_Radius;
float z = Mathf.Cos(i * m_Rad + m_MoveRad) * m_Radius;
if (m_List.Count <= i)
{
GameObject go = Instantiate(m_Prefab, transform);
go.GetComponent<Image>().sprite = Resources.Load<Sprite>(partName + "/" + i.ToString());
go.name = i.ToString();
m_List.Add(go);
m_Trans.Add(go.transform);
}
m_List[i].transform.localPosition = new Vector3(x, 0, 0);
float scale = (z + m_Radius) / (2 * m_Radius) * 0.5f + 0.5f;
m_List[i].transform.localScale = Vector3.one * scale;
}
//通过localScale的z值排序
m_Trans.Sort((a, b) =>
{
return (int)(a.localScale.z * 100 - b.localScale.z * 100);
});
//排序后更改unity中的层级关系
for (int i = 0; i < m_Trans.Count; i++)
{
m_Trans[i].SetSiblingIndex(i);
}
}
public void OnDrag(PointerEventData eventData)
{
m_MoveRad += eventData.delta.x / m_Radius;
OnMove();
}
public void OnEndDrag(PointerEventData eventData)
{
DT.To((a) =>
{
//惯性
m_MoveRad += a;
OnMove();
}, eventData.delta.x, 0, 1.5f).OnComplete(() =>
{
//对齐
float offsetRad = Mathf.Asin(m_Trans[m_Num - 1].localPosition.x / m_Radius);
DT.To((a) =>
{
m_MoveRad = a;
OnMove();
}, m_MoveRad, m_MoveRad - offsetRad, 1).OnComplete(() =>
{
//对齐结束后换装
if (partName == "Head")
{
changeAvator.m_Objs[0] = Resources.Load<GameObject>("Prefab/Tou_" + m_Trans[m_Num - 1].GetComponent<Image>().sprite.name);
changeAvator.Change();
}
else if (partName == "Leg")
{
changeAvator.m_Objs[1] = Resources.Load<GameObject>("Prefab/Tui_" + m_Trans[m_Num - 1].GetComponent<Image>().sprite.name);
changeAvator.Change();
}
else if (partName == "Clothes")
{
changeAvator.m_Objs[2] = Resources.Load<GameObject>("Prefab/YiFu_" + m_Trans[m_Num - 1].GetComponent<Image>().sprite.name);
changeAvator.Change();
}
});
});
}
}
下面是蒙皮与换骨骼脚本代码。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChangeAvator : MonoBehaviour
{
public List<GameObject> m_Objs;//人物部位部件
private void Start()
{
gameObject.AddComponent<SkinnedMeshRenderer>();
Change();
}
public void Change()
{
#region 蒙皮网格渲染器的属性赋值
List<CombineInstance> combines = new List<CombineInstance>();
List<Material> materials = new List<Material>();//材质集合
foreach (var item in m_Objs)//遍历所有人物部件
{
CombineInstance com=new CombineInstance();
com.mesh = item.GetComponentInChildren<SkinnedMeshRenderer>().sharedMesh;//每个部件上的 网格信息 赋值给合并器
combines.Add(com);//添加到集合中
materials.Add(item.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial);//每个部件上的 材质信息 合并到材质集合中
}
Mesh mesh = new Mesh();//新建一个网格
mesh.CombineMeshes(combines.ToArray(), false, false);//合并网格数组,赋值给网格对象
gameObject.GetComponent<SkinnedMeshRenderer>().sharedMesh = mesh;//给皮肤网格渲染器赋值网格信息
gameObject.GetComponent<SkinnedMeshRenderer>().sharedMaterials = materials.ToArray();//给皮肤网格赋值材质信息
#endregion
#region 合并骨骼,动画基于骨骼
Transform[] allBones = GetComponentsInChildren<Transform>();//挂载此脚本游戏对象的所有Transform组件
Dictionary<string, Transform> bonesDiC = new Dictionary<string, Transform>();//骨骼字典
foreach (var item in allBones)
{
bonesDiC.Add(item.name, item);//添加到骨骼字典
}
List<Transform> bones = new List<Transform>();//存储需要改变的部位骨骼
foreach (var item in m_Objs)//遍历部位模型,在轮转图中,被选中的人物部件,把骨骼信息对应上
{
Transform[] bodyBones = item.GetComponentInChildren<SkinnedMeshRenderer>().bones;//获取部位模型身上的所有骨骼
foreach (var item2 in bodyBones)//遍历部位模型身上的所有骨骼
{
if (bonesDiC.ContainsKey(item2.name))//骨骼字典中包含部位模型中的骨骼
{
//这里千万不要写成 bones.Add(item2); 否则后续骨骼移动会出现大问题!
bones.Add(bonesDiC[item2.name]);//将部位模型中的骨骼添加到需要改变的部位骨骼集合
}
}
}
gameObject.GetComponent<SkinnedMeshRenderer>().bones = bones.ToArray();//骨骼数据赋值给皮肤网格渲染器
#endregion
}
}