这里合并网格的实现方法是,使用三个已有的预制体,通过预制体的SkinnedMeshRenderer组件和CombineInstance来实现
1.准备三个部位的预制体(Cube用来挂脚本)
2.Cube挂上CombineMesh脚本,声明公开的SkinnedMeshRenderer组件,将三个预制体带有SkinnedMeshRenderer组件的部分拖到里面
3.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CombineMesh : MonoBehaviour
{
public SkinnedMeshRenderer[] sk; // 存储需要合并的SkinnedMeshRenderer组件的数组
void Start()
{
Mesh mesh = new Mesh(); // 用于存储合并后的网格数据
List<CombineInstance> combines = new List<CombineInstance>(); // 用于存储需要合并的网格实例数据
List<Texture2D> textures = new List<Texture2D>(); // 用于存储需要合并的纹理数据
// 遍历所有的SkinnedMeshRenderer
for (int i = 0; i < sk.Length; i++)
{
CombineInstance instance = new CombineInstance();
instance.mesh = sk[i].sharedMesh; // 获取当前SkinnedMeshRenderer的共享网格数据
instance.transform = sk[i].transform.localToWorldMatrix; // 获取当前SkinnedMeshRenderer的局部到世界的变换矩阵
combines.Add(instance); // 将网格实例数据加入到列表中
textures.Add(sk[i].sharedMaterial.GetTexture("_BackTex") as Texture2D); // 获取当前SkinnedMeshRenderer的纹理数据,并加入到纹理列表中
}
mesh.CombineMeshes(combines.ToArray()); // 合并网格实例数据
//合并纹理
Texture2D texture = new Texture2D(0, 0); // 创建新的纹理对象
Rect[] rects = texture.PackTextures(textures.ToArray(), 0); // 将纹理数据打包到一个纹理中,并返回纹理矩形数据
//通过矩形数据rects重新计算uv坐标
List<Vector2> uv = new List<Vector2>(); // 存储重新计算后的UV坐标数据
for (int i = 0; i < sk.Length; i++)
{
Vector2[] olduv = sk[i].sharedMesh.uv; // 获取当前SkinnedMeshRenderer的共享网格的UV坐标数据
for (int j = 0; j < olduv.Length; j++)
{
float uvx = rects[i].x + rects[i].width * olduv[j].x; // 计算重新映射后的UV坐标的X值
float uvy = rects[i].y + rects[i].height * olduv[j].y; // 计算重新映射后的UV坐标的Y值
uv.Add(new Vector2(uvx, uvy)); // 将重新映射后的UV坐标加入到列表中
}
}
mesh.uv = uv.ToArray(); // 将重新计算后的UV坐标数据赋值给网格
GetComponent<MeshFilter>().mesh = mesh; // 将合并后的网格赋值给当前物体的MeshFilter组件
Texture2D face = sk[0].sharedMaterial.GetTexture("_MainTex") as Texture2D; // 获取第一个SkinnedMeshRenderer的主纹理
Material material = new Material(Shader.Find("Custom/Face")); // 创建新的材质对象,并使用自定义的Face着色器
material.SetTexture("_BackTex", texture); // 设置背景纹理
material.SetTexture("_MainTex", face); // 设置主纹理
material.SetFloat("_PosX", texture.width / face.width); // 设置X方向的纹理缩放比例
material.SetFloat("_PosY", texture.height / face.height); // 设置Y方向的纹理缩放比例
GetComponent<MeshRenderer>().material = material; // 将新的材质赋值给当前物体的MeshRenderer组件
}
void Update()
{
}
}
完成轮转图换装
声明一个SkinnedMeshRenderer数组的集合,保存不同的部位预制体
把Start内的合并过程封装成方法,方法需要一个形参作为所需SkinnedMeshRenderer数组的下标
public void Change(int index)
{
Mesh mesh = new Mesh(); // 用于存储合并后的网格数据
List<CombineInstance> combines = new List<CombineInstance>(); // 用于存储需要合并的网格实例数据
List<Texture2D> textures = new List<Texture2D>(); // 用于存储需要合并的纹理数据
// 遍历所有的SkinnedMeshRenderer
for (int i = 0; i < lst[index].Length; i++)
{
CombineInstance instance = new CombineInstance();
instance.mesh = lst[index][i].sharedMesh; // 获取当前SkinnedMeshRenderer的共享网格数据
instance.transform = lst[index][i].transform.localToWorldMatrix; // 获取当前SkinnedMeshRenderer的局部到世界的变换矩阵
combines.Add(instance); // 将网格实例数据加入到列表中
textures.Add(lst[index][i].sharedMaterial.GetTexture("_BackTex") as Texture2D); // 获取当前SkinnedMeshRenderer的纹理数据,并加入到纹理列表中
}
mesh.CombineMeshes(combines.ToArray(), true, false); // 合并网格实例数据
//合并纹理
Texture2D texture = new Texture2D(0, 0); // 创建新的纹理对象
Rect[] rects = texture.PackTextures(textures.ToArray(), 0); // 将纹理数据打包到一个纹理中,并返回纹理矩形数据
//通过矩形数据rects重新计算uv坐标
List<Vector2> uv = new List<Vector2>(); // 存储重新计算后的UV坐标数据
for (int i = 0; i < lst[index].Length; i++)
{
Vector2[] olduv = lst[index][i].sharedMesh.uv; // 获取当前SkinnedMeshRenderer的共享网格的UV坐标数据
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;
uv.Add(new Vector2(uvx, uvy)); // 将重新映射后的UV坐标加入到列表中
}
}
mesh.uv = uv.ToArray(); // 将重新计算后的UV坐标数据赋值给网格
// GetComponent<MeshFilter>().mesh = mesh; // 将合并后的网格赋值给当前物体的MeshFilter组件
GetComponent<SkinnedMeshRenderer>().sharedMesh = mesh; // 将合并后的网格赋值给当前物体的MeshFilter组件
Texture2D face = lst[index][0].sharedMaterial.GetTexture("_MainTex") as Texture2D; // 获取第一个SkinnedMeshRenderer的主纹理
Material material = new Material(Shader.Find("Custom/Face")); // 创建新的材质对象,并使用自定义的Face着色器
material.SetTexture("_BackTex", texture); // 设置背景纹理
material.SetTexture("_MainTex", face); // 设置主纹理
material.SetFloat("_PosX", texture.width / face.width); // 设置X方向的纹理缩放比例
material.SetFloat("_PosY", texture.height / face.height); // 设置Y方向的纹理缩放比例
GetComponent<SkinnedMeshRenderer>().material = material; // 将新的材质赋值给当前物体的MeshRenderer组件
List<Transform> bones = new List<Transform>();
Transform[] mybones = GetComponentsInChildren<Transform>();
Dictionary<string, Transform> dicbones = new Dictionary<string, Transform>();
foreach (var item in mybones)
{
dicbones.Add(item.name, item);
}
for (int i = 0; i < lst[index].Length; i++)
{
for (int j = 0; j < lst[index][i].bones.Length; j++)
{
if (dicbones.ContainsKey(lst[index][i].bones[j].name))
{
bones.Add(dicbones[lst[index][i].bones[j].name]);
}
}
}
GetComponent<SkinnedMeshRenderer>().bones = bones.ToArray();
}
代码实现轮转图,旋转时根据距离屏幕最近的物体下标更换avata的数组
挂轮转图代码的游戏对象需要放在Canvas下面, prefab是有一个Text子类的Image
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using TMPro;
using DG.Tweening;
public class Rolling2D : MonoBehaviour, IDragHandler, IEndDragHandler
{
public int num; //物体数量
public float space = 20; //物体间距
public Image prefab; //预制体
float l;//周长
float r;//半径
float angle; //每个角的弧度
float allangle = 0; //移动的弧度
float max = 1; //最大值
float min = 0.5f; //最小值
public float dec = 10; //减速度
public List<GameObject> list = new List<GameObject>(); //存放游戏对象
public List<Transform> sort = new List<Transform>(); //排序用
public GameObject avator;
void Start()
{
// btn.onClick.AddListener(OnBtn);
l = num * (prefab.rectTransform.sizeDelta.x + space);
r = l / (2 * Mathf.PI);
angle = 2 * Mathf.PI / num;
Move();
}
public void Move()
{
for (int i = 0; i < num; i++)
{
if (list.Count <= i)
{
list.Add(Instantiate(prefab.gameObject, transform));
sort.Add(list[i].transform);
list[i].transform.GetComponentInChildren<TextMeshProUGUI>().text = i.ToString();
}
float x = Mathf.Sin(i * angle + allangle) * r;
float z = Mathf.Cos(i * angle + allangle) * r;
float p = (z + r) / (r + r);
float scale = (max - p) + min * p;
list[i].transform.localPosition = new Vector3(x, 0, 0);
list[i].transform.localScale = Vector3.one * scale;
}
Sort();
}
public void Sort()
{
sort.Sort((a, b) => { return (int)(a.localScale.z * 10 - b.localScale.z * 10); });
for (int i = 0; i < sort.Count; i++)
{
sort[i].SetSiblingIndex(i);
}
int index = list.IndexOf(sort[sort.Count - 1].gameObject);
avator.GetComponent<CombineMesh>().Change(index);
}
public void OnDrag(PointerEventData eventData)
{
allangle -= eventData.delta.x / r;
Move();
}
public void OnEndDrag(PointerEventData eventData)
{
float dis = eventData.delta.x;
DOTween.To(() => dis, (t) =>
{
allangle -= t / r;
Move();
}, 0, 2).OnComplete(() =>
{
float moveang = Mathf.Asin(sort[num - 1].localPosition.x / r);
float movetime = Mathf.Abs(moveang * r / dec);
DOTween.To(()=> allangle, (b) =>
{
allangle = b;
Move();
}, allangle+moveang,1);
});
}
// Update is called once per frame
void Update()
{
}
}