网上的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

Unity插件 - MeshEditor(三) 面片破碎&网格破碎的更多相关文章

  1. Unity插件 - MeshEditor(五) 网格顶点动画(变形动画)

    源码已上传至github,并持续更新,链接请看底部.(本帖跟随github持续更新) 网格顶点动画(变形动画)是针对于物体的形状可以随意变换并记录为关键帧的动画,虽然模型的顶点数据还是应该交给GPU绘 ...

  2. Unity插件 - MeshEditor(二) 模型网格编辑器(高级)

    源码已上传至github,并持续更新,链接请看底部.(本帖跟随github持续更新) 继先前的一篇MeshEditor之后,MeshEditor第二版发布,这次在先前的基础上加入了为模型新增顶点以及删 ...

  3. Unity插件 - MeshEditor(一) 3D线段作画 & 模型网格编辑器

    之前,因为工作需要,项目中需要动态生成很多的电线,不能事先让模型做好,更不能用LineRenderer之类的,因为画出来没有3D的效果,最主要是拐角的时候还容易破面,而我们要的是真真实实纯3D的电线, ...

  4. Unity插件 - MeshEditor(七)变形动画骨骼及蒙皮

    MeshAnimation在物体的顶点比较多的情况下,悲剧是显而可见的,我一个一个的点选顶点肯定得累死,而且对于形态的调控不是很方便,应该说是很麻烦,要知道,骨骼动画因为有了骨骼以及蒙皮信息而有了灵魂 ...

  5. Unity插件 - MeshEditor(六) 变形动画状态机

    变形动画状态机--MeshAnimator,是针对MeshAnimation的状态管理器,有大量类似Unity animator的功能,但MeshAnimator操作会更加简便,更加直观,居家旅(zh ...

  6. Unity插件 - MeshEditor(八)模型镜像特效

    将静态模型(带MeshFilter)按指定轴向.指定距离克隆一个镜像物体出来,思路很简单,将模型的顶点坐标按指定轴取反,并累加上设定的距离值,然后就完毕了!不过,因为镜像体的顶点镜像于之前模型的顶点, ...

  7. Unity插件 - MeshEditor(四) 模型融化特效

    现在的电影里有很多妖魔在死亡后身体逐渐融化并下滑最后化为一滩黑水的情景,本次出于兴趣大致研究了这个效果,原理是控制模型的顶点向一个方向坍塌,坍塌到最低点时再根据法线方向扩散形成黑水状. 第一步: 添加 ...

  8. Unity插件之Unity调用C#编译的DLL

    Unity插件分为两种:托管插件(Managed Plugins)和本地插件(Native Plugins).本文先来说说Unity中的托管插件,本地插件的文章留到下一篇文章再说. 有时候我们会有这样 ...

  9. 《图说VR入门》——Unity插件DK2使用教程

    本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/53339254 作者:car ...

随机推荐

  1. python文件转exe

    .py文件转exe文件 1.软件说明: 用python写一个视频处理软件,用到的库是moviepy 2.所用软件: Python 3.6.5 32位 pycharm  专门的python编辑ide,推 ...

  2. bash的工作特性及其使用方法

    bash的工作特性之命令执行状态返回值和命令展开所涉及的内容及其示例演出 !脚本执行与调试1.绝对路径执行,要求文件有执行权限2.以sh命令执行,不要求文件有执行权限3..加空格或source命令执行 ...

  3. IP地址段遍历

    #region 搜索ftp服务器地址 /// <summary> /// 搜索ftp服务器 /// </summary> public void SearchFtpServer ...

  4. c++指针函数的使用——回调函数

    /* 函数指针 函数也是有地址的 所谓函数指针,就是指向函数的指针,函数指针也是一个变量,可以指向不同的函数.同时通过函数指针可以调用其指向函数,从而使函数的调用更加灵活. 函数指针的用途 */ #i ...

  5. 漫谈Web缓存架构

    计算机领域多处地方用到缓存,比如说为了缓解CPU和内存之间的速度不匹配问题,我们往往通过增加一级.二级.三级缓存,CPU先从缓存中取指令,如果取不到,再从内存中取,并更新缓存,同时,根据程序的局部性原 ...

  6. Rstudio-server 重新登录载入session的问题

    Rstudio-server 非常好用,但是免费版的也有一些问题,一个是只能支持在一个客户端的登录,另一个就是每次登录都要导入之前session的问题,对于第二个问题的解决方案,我们可以进入到~/.r ...

  7. Luxurious Houses

    The capital of Berland has n multifloor buildings. The architect who built up the capital was very c ...

  8. 分别用face++和百度获取人脸属性(python单机版)

    称之为单机版,主要是相对于调用摄像头实时识别而言.本篇主要py2下利用face++和百度接口获取本地图片中的人脸属性,并按照一定格式保存数据. face++版 face++是刚注册的,只能用一个试用的 ...

  9. sql server 的osql 使用例子

    一次性执行一个语句并输出到文件: osql -Usa -dSCGWYDJ2012 -P -S192.168.56.1 -Q "exit( SELECT top 1 * FROM A08)&q ...

  10. Bootstrap3 表格-条纹状表格

    通过 .table-striped 类可以给 之内的每一行增加斑马条纹样式. 跨浏览器兼容性 条纹状表格是依赖 :nth-child CSS 选择器实现的,而这一功能不被 Internet Explo ...