zoukankan      html  css  js  c++  java
  • Unity插件

    网上的unity破碎插件很多,不过想着可以以自己的方式实现也不失为一种乐趣,虽然整体的表现性上显得有些差,但也并不会影响最终的效果,接下来我大致讲解一下破碎一个物体的流程,因为用到了协程计算碎片的原因,所以会在所有碎片计算完成以后才会触发碎片的物理效果,所以有些模型可能会显得卡顿一下。



    第一步:


    添加MeshBroken脚本,目前破碎参数还较少,可以进行一些适当的调节以达到不同的破碎效果,不过建议面数过多的物体慎用,因为这里破碎的碎块数量必须大于等于原网格的面数(至于为什么呢,那是因为我目前还没想到可以数目随机、位置随机的组合多个三角面的方式,如果有相关思路的欢迎一起讨论)。


    IsBrokenOnHit:物体受到碰撞时自动破碎,无论是实际碰撞还是触发器碰撞;

    FragmentNum:碎片数量;

    SurfaceFragmentThickness:面片破碎时单个面片厚度;

    GridFragmentMinThickness:块状破碎时单个碎块厚度变化最小值;

    GridFragmentMaxThickness:块状破碎时单个碎块厚度变化最大值;

    FragmentStyle:碎片类型,可选择surface(面片破碎),grid(块状破碎);

    BrokenStyle:破碎方式,可选择statice(静态破碎),explode(爆炸破碎);

    ExplosiveForce:爆炸破碎时的爆炸力;

    ExplosionRange:爆炸破碎时的爆炸力作用范围;



    第二步:


    效果演示。






    第三步:


    实现的大致流程。

    首先,开始破碎时,我们先要计算所有的碎片组成(所以比较耗性能,破碎过程是即时计算的),因为目前的破碎方式是按模型原本面数进行计算的,如果模型的面数小于碎片需求,则需要按一定的规则破开部分面

    /// <summary>
        /// 计算所有碎片
        /// </summary>
        List<List<List<int>>> ComputeAllFragment()
        {
            List<List<List<int>>> allFragment = new List<List<List<int>>>();
            //碎片数量大于面数,则破开一定的面
            if (_FragmentNum > _AllTriangleList.Count)
            {
                int num = _FragmentNum - _AllTriangleList.Count;
                while (num > 0)
                {
                    CutApartSurface(_AllTriangleList[Random.Range(0, _AllTriangleList.Count)]);
                    num -= 1;
                }
            }
            //按模型面数进行破碎
            for (int i = 0; i < _AllTriangleList.Count; i++)
            {
                List<List<int>> fragment = new List<List<int>>();
                fragment.Add(_AllTriangleList[i]);
                allFragment.Add(fragment);
            }
            return allFragment;
        }



    破开一个面的方式

    /// <summary>
        /// 将一个面割开
        /// </summary>
        /// <param name="triangle">面片</param>
        void CutApartSurface(List<int> triangle)
        {
            Vector3 vertices0 = _AllVerticesList[triangle[0]];
            Vector3 vertices1 = _AllVerticesList[triangle[1]];
            Vector3 vertices2 = _AllVerticesList[triangle[2]];
            //计算新顶点坐标
            Vector3 newVertex = new Vector3((vertices1.x - vertices2.x) / 2.0f + vertices2.x,
                (vertices1.y - vertices2.y) / 2.0f + vertices2.y,
                (vertices1.z - vertices2.z) / 2.0f + vertices2.z);
            //计算新顶点的UV
            Vector2[] uv = _Uv;
            Vector2[] Newuv = new Vector2[uv.Length + 1];
            for (int i = 0; i < uv.Length; i++)
            {
                Newuv[i] = uv[i];
            }
            Newuv[Newuv.Length - 1] = new Vector2((uv[triangle[1]].x - uv[triangle[2]].x) / 2 + uv[triangle[2]].x
                , (uv[triangle[1]].y - uv[triangle[2]].y) / 2 + uv[triangle[2]].y);
            _Uv = Newuv;
            //计算新顶点的法线
            Vector3[] normal = _Normal;
            Vector3[] Newnormal = new Vector3[normal.Length + 1];
            for (int i = 0; i < normal.Length; i++)
            {
                Newnormal[i] = normal[i];
            }
            Newnormal[Newnormal.Length - 1] = _Normal[triangle[2]];
            _Normal = Newnormal;
            //新顶点加入所有顶点集合
            _AllVerticesList.Add(newVertex);
            //记录新顶点索引
            int _index = _AllVerticesList.IndexOf(newVertex);
            //割开三角面
            List<int> newTriangle1 = new List<int>();
            List<int> newTriangle2 = new List<int>();
            newTriangle1.Add(triangle[0]);
            newTriangle1.Add(triangle[1]);
            newTriangle1.Add(_index);
            newTriangle2.Add(_index);
            newTriangle2.Add(triangle[2]);
            newTriangle2.Add(triangle[0]);
            _AllTriangleList.Remove(triangle);
            _AllTriangleList.Add(newTriangle1);
            _AllTriangleList.Add(newTriangle2);
        }


    所有的碎片组成方式都计算完毕之后,便保存所有碎片集合,遍历集合以生成所有碎片

    /// <summary>
        /// 生成网格碎片
        /// </summary>
        void ProduceGridFragment(List<List<int>> fragment)
        {
            //新建碎片物体
            GameObject obj = new GameObject(transform.name + "_fragment" + transform.childCount);
            obj.transform.position = transform.position;
            obj.transform.rotation = transform.rotation;
            obj.transform.localScale = transform.localScale;
            obj.transform.SetParent(transform);
            obj.AddComponent<MeshFilter>();
            obj.AddComponent<MeshRenderer>();
            obj.GetComponent<MeshRenderer>().material = GetComponent<MeshRenderer>().material;
    
            Mesh _mesh = new Mesh();
            _mesh.Clear();
            //不合法的碎片
            if (fragment.Count < 1 || fragment[0].Count < 3)
                return;
            //计算碎片的顶点
            Vector3[] _fragment = new Vector3[] {
                _AllVerticesList[fragment[0][0]],
                _AllVerticesList[fragment[0][1]],
                _AllVerticesList[fragment[0][2]],
                _AllVerticesList[fragment[0][2]] - _Normal[fragment[0][2]] * Random.Range(_GridFragmentMinThickness,_GridFragmentMaxThickness),
                _AllVerticesList[fragment[0][1]] - _Normal[fragment[0][2]] * Random.Range(_GridFragmentMinThickness,_GridFragmentMaxThickness),
                _AllVerticesList[fragment[0][0]] - _Normal[fragment[0][2]] * Random.Range(_GridFragmentMinThickness,_GridFragmentMaxThickness)
            };
            //计算碎片的三角面
            int[] _triangle = new int[] {
                0,1,2,
                3,4,5,
                2,1,4,
                4,3,2,
                0,2,3,
                0,3,5,
                5,4,1,
                5,1,0
            };
            //计算碎片的uv
            Vector2[] _uv = new Vector2[] {
                _Uv[fragment[0][0]],
                _Uv[fragment[0][1]],
                _Uv[fragment[0][2]],
                _Uv[fragment[0][2]],
                _Uv[fragment[0][1]],
                _Uv[fragment[0][0]]
            };
            _mesh.vertices = _fragment;
            _mesh.triangles = _triangle;
            _mesh.uv = _uv;
            //生成碎片
            _mesh.name = transform.name + "_fragment" + transform.childCount;
            _mesh.RecalculateNormals();
            obj.GetComponent<MeshFilter>().mesh = _mesh;
            _Fragment.Add(obj);
        }



    之后在其他脚本里,想要动态的控制物体的破碎的话,可以在外部调用破碎开关函数

    /// <summary>
        /// 开始破碎
        /// </summary>
        public void BeginBroken()
        {
            if (IsCanBroken)
            {
                StartCoroutine(BroKening());
                IsCanBroken = false;
            }
            else
                Debug.Log("由于未知原因,目标无法破碎!");
        }


    最后附上动态演示图:




    -----by MeshEditor

  • 相关阅读:
    Linux程序分析工具介绍—ldd,nm
    Makefile学习(三)[第二版]
    Linux下的tree命令 --Linux下目录树查看
    Makefile学习(二)[第二版]
    Makefile学习(一)[第二版]
    Linux下top命令详解
    Shell编程入门(第二版)(下)
    mysql用命令行导入sql文件
    javascript的onbeforeunload函数在IOS上运行
    mysql如何利用Navicat 导出和导入数据库
  • 原文地址:https://www.cnblogs.com/liang123/p/6325872.html
Copyright © 2011-2022 走看看