Unity 合并网格和轮转图换装

这里合并网格的实现方法是,使用三个已有的预制体,通过预制体的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()
    {

    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值