First you need to know how to rotate 2D graphics.
Achieve costume changes through 2D rotation maps, skinned mesh renderers and merged bones.
The first is the association between the 2D rotation map and the skinning script, the changeAvator.Change() method in the code below.
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;//Picture prefab int m_Num;//number of pictures int indexHead = 11; int indexLeg = 5; int indexClothes = 4; float m_Rad;//radians string partName = "";//Part name public float m_Spacing; float m_Radius;//Radius float m_MoveRad;//Move radians public ChangeAvator changeAvator; private void Start() { //Intercept part information 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; } //Sort by z value of localScale m_Trans.Sort((a, b) => { return (int)(a.localScale.z * 100 - b.localScale.z * 100); }); //Change the hierarchical relationship in unity after sorting 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) => { //inertia m_MoveRad + = a; OnMove(); }, eventData.delta.x, 0, 1.5f).OnComplete(() => { //align 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(() => { //Change after alignment 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(); } }); }); } }
The following is the skinning and bone replacement script code.
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class ChangeAvator : MonoBehaviour { public List<GameObject> m_Objs;//character parts private void Start() { gameObject.AddComponent<SkinnedMeshRenderer>(); Change(); } public void Change() { #region Attribute assignment of skinned mesh renderer List<CombineInstance> combines = new List<CombineInstance>(); List<Material> materials = new List<Material>();//Material collection foreach (var item in m_Objs)//Traverse all character parts { CombineInstance com=new CombineInstance(); com.mesh = item.GetComponentInChildren<SkinnedMeshRenderer>().sharedMesh;//Assign the mesh information on each component to the combiner combines.Add(com);//Add to the collection materials.Add(item.GetComponentInChildren<SkinnedMeshRenderer>().sharedMaterial);//The material information on each component is merged into the material collection } Mesh mesh = new Mesh();//Create a new mesh mesh.CombineMeshes(combines.ToArray(), false, false);//Combine mesh arrays and assign values to mesh objects gameObject.GetComponent<SkinnedMeshRenderer>().sharedMesh = mesh;//Assign mesh information to the skin mesh renderer gameObject.GetComponent<SkinnedMeshRenderer>().sharedMaterials = materials.ToArray();//Assign material information to the skin mesh #endregion #region merges bones, animation is based on bones Transform[] allBones = GetComponentsInChildren<Transform>();//Mount all Transform components of this script game object Dictionary<string, Transform> bonesDiC = new Dictionary<string, Transform>();//Bone dictionary foreach (var item in allBones) { bonesDiC.Add(item.name, item);//Add to bone dictionary } List<Transform> bones = new List<Transform>();//Storage the bones that need to be changed foreach (var item in m_Objs)//Traverse the part model, in the rotation diagram, the selected character parts correspond to the bone information { Transform[] bodyBones = item.GetComponentInChildren<SkinnedMeshRenderer>().bones;//Get all the bones on the part model foreach (var item2 in bodyBones)//Traverse all bones on the body model { if (bonesDiC.ContainsKey(item2.name))//The bone dictionary contains the bones in the part model { //Do not write bones.Add(item2); otherwise there will be big problems with subsequent bone movement! bones.Add(bonesDiC[item2.name]);//Add the bones in the part model to the part bone collection that needs to be changed } } } gameObject.GetComponent<SkinnedMeshRenderer>().bones = bones.ToArray();//Assign bone data to the skin mesh renderer #endregion } }