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

  • 相关阅读:
    Uva 10779 collector's problem
    poj 2728 最优比率树(最小生成树问题)
    LA 3126 二分图匹配 最小路径覆盖
    poj 1149 最大流构图
    Step By Step(Java XML篇)
    Step By Step(Java 输入输出篇)
    Step By Step(Java 集合篇)
    Step By Step(Java 线程篇)
    Step By Step(Java 反射篇)
    Step By Step(Java 国际化篇)
  • 原文地址:https://www.cnblogs.com/liang123/p/6325872.html
Copyright © 2011-2022 走看看