我是的十八簿 发表于 2021-1-28 13:39

游戏破坏系统简介

游戏内的破坏效果这两年开始逐渐被广泛应用。Nvidia 和Havok在几年前已经提供各自的破坏中间件。知名游戏引擎UE4 更是在2019年GDC宣布官方实现的原生破坏系统。相信很快破坏系统会在很多游戏中得到更广泛的应用。
游戏破坏系统通常作为物理引擎的一部分,可以提升游戏表现效果,此外在少数游戏中,破坏系统会作为游戏玩法的核心构成之一,给玩家带来无与伦比的真实体验。
一、物理破坏的发展
早在电子游戏刚起步的年代,设计者们就在关卡中引入破坏元素了,从早期的《超级马里奥》,吃下蘑菇之后顶砖头;到《魂斗罗》中开局会自爆的桥,到各色各样的打砖块游戏;再到2000年之后,例如《英雄萨姆1》里,树木场景里的道具都可以被击碎;再到今年各种3A游戏中的破坏元素如生化危机7,最终boss钙化成碎片;再到最近的《赛博朋克2077》里的,主角V去救人但是不小心暴露了,硬刚加特林大哥,大哥直接轰碎墙壁来干V,仿佛真的一面墙在你面前轰然倒下。随着一代又一代的沉淀,游戏破坏的画面表现力越来越好,游戏体验也越来越强。那么现在常用的物理破坏引擎有哪些?一个个逼真的物理破坏效果是怎么做出来的呢?今天就随笔者来一探究竟吧。
二、主流破坏引擎介绍
现代游戏工作室都是工业化制作游戏的,开发者通常用Unity、Unreal这两个商业引擎或者自研引擎进行游戏开发。商业引擎普遍使用的是已存在的物理引擎,破坏引擎。自研引擎也是一样,通常是集成一些已有的物理引擎、破坏引擎来做的。只有极少数项目会尝试自研物理引擎和自研破坏引擎。
所以本文不展开讨论物理引擎,只讨论破坏引擎本身。下面着重介绍一些主流的破坏引擎:
(1)Nvidia APEX destruction
此APEX并非最近热火的滑铲APEX Legends,而是前PhysX配套的破坏引擎。
为什么说是前PhysX配套的破坏引擎呢,因为APEX destruction 2017年就deprecated,被Nvidia Blast库替代。但它现在还被主流商业引擎Unreal使用。Epic现在最新的Unreal 4.26,它依然是默认的破坏引擎,全套工具链都被Epic官方支持。官方集成比开发者自己动手集成要靠谱得多,一般测试覆盖更全,工具链更全,资源生产pipeline整合得更优。因此可以预计在Chaos正式上线之前,APEX Destruction还是Unreal引擎的最优选择之一。
(2)Nvidia Blast
Nvidia Blast是APEX Destruction的继任者,是PhysX配套的新一代破坏引擎。Nvidia为游戏套件贡献过不少优秀的库,Blast就是其中之一。
现在市面上所有能用的开源破坏引擎当中,它算是最新最合适的。
Blast库相较于老旧的APEX destruction,有很多优点:
1. 支持较多新特性,如用户可以指定不同深度的碎片作为支撑碎片。
2. 底层API设计简单,其接口都是类C的无状态函数,没有全局上下文,其API函数内也不包含task创建,内存配分与释放。
3. 没有和物理引擎强绑定,也就是说,即使你没在使用PhysX,你依然可以把Blast集成到你自己的物理引擎中。
4. 运行效率相比APEX destruction,提升很多。
虽然与其前辈相比有很多优点,但Blast库在笔者看来依然有一些令人遗憾的点:
1. Blast库虽然还在维护,还没deprecated,但是更新速度并不算快
2. 没有非常好用的美术资产生产工具
3. 对于商业引擎,无论unity还是unreal,都没有引擎官方的集成。
4. Nvidia官方在两个引擎上都做过集成,但集成比较老旧,且以Unreal为例,blast库的集成并非完全以插件形式放入,引擎代码里也有不少改动,最新的引擎版本只到4.20。如果开发者想用最新UE引擎+Blast库,会有一些版本升级的成本。
5. 以Unreal的blast集成为例, blast版Unreal引擎在设计上是有缺陷的,例如运行时crash的bug。
(3)Havok destruction
如果你是一个游戏爱好者,你可能没听过Havok,但是你一定见过下面这些游戏:
这些主动物理表现的3A游戏,它背后的物理引擎都是Havok。
而Havok号称是物理引擎之神,没有之一,和它相比PhysX连弟弟都算不上。
新一点的Unity已经可以使用Havok作为物理引擎,关于这点,我们相信Unity官方在背后付出了很多努力,很赞!
早些年havok引擎涵盖面比较广,包含物理、破坏、动作、动画、AI、柔布。
但这个公司命途多舛,几经收购之后现在落入Microsoft手中,产品线砍得只剩下Physics,AI,Cloth。
Havok Destruction的优点是工具链很全,运行时很高效,与破坏相关的特性极多。
缺点是授权费用高昂,作为开发者,接触到它的机会比较少。
(4)Chaos Destruction
长久以来,Unreal使用PhysX作为其默认的物理引擎,并且集成了APEX Cloth、 APEX destruction来提升表现效果(对于一些体量较大的合作伙伴,Unreal也提供了官方支持的Havok物理引擎版,当然前提是你和Havok方也签过license)。
对于unreal这样的重量级的游戏引擎来说,仅仅使用PhysX及其套件,并不能给其用户带来最先进的开发工具。
虽然unreal官方有对Havok的集成,但前提是你向havok方买过license,对于大多数中小甚至个人开发者,都不满足这样的条件。
另一边,与git上Unreal引擎的更新速度相对比,PhysX、Cloth、Destruction这些重要中间件的开发速度,要么慢如乌龟,要么中途deprecated。可能是因为以上原因,unreal决定自己搞物理引擎,这对于unreal来说是迈得很大的一步。
物理引擎及其套件是有比较高的技术壁垒的,开发周期会动辄以半年/年来计。
Chaos最早于19年GDC上亮相,当时UE版本是4.22,其视频的表现力很震撼的。到现在UE 4.26 release,时间过去一年半多,chaos刚处于beta阶段,依然还不是引擎默认的物理引擎。乐观一点来看,至少还要一年半载的,chaos能初步可用。
如果你的项目使用unreal引擎开发的,并且将在未来1年半以后才会上线,并且没有对物理引擎的深入修改。那么推荐你拿到最新代码,现在就打开chaos开关用起来。否则的话就别换了。
三、破坏系统基本原理
(1)游戏物理基础
因为破坏引擎通常是物理引擎的一部分,并不会单独存在,所以介绍破坏引擎,不得不提到物理引擎。这部分不是本文重点要关注的地方,所以下面简单介绍一下。
在现实生活中,球扔出后会有抛物线的运动轨迹,球落到地上会以一定角度反弹,这都是给定初速度下球受力及碰撞后的综合结果。那么在游戏中,我们如何模拟这个过程呢?这就需要物理引擎大展身手了。
大多数游戏物理引擎都是对牛顿物理的模拟,对刚体及刚体运动的模拟。游戏物理引擎中的每个刚体,通常有以下信息:
有描述类型的信息,如静态、动态、Keyframed(kinematic);
2. 有描述运动的信息,如transform、线速度、角速度;
3. 有描述形状信息,如方形、圆形、胶囊体型、凸包型;
4. 有描述质量信息,如质心位置,质量大小;
5. 有描述材质信息,摩擦系数、弹性系数。
下面我们来看一下物理引擎模拟的流程。
如上图所示,大体上分为Collide(碰撞检测)和Solve(约束求解)两步。
【Collide】阶段主要分为以下几个步骤:
1. Broad Phase:粗略判断可能的碰撞,生成Body Pair 送入Narrow Phase进行更细致的比对。Broad Phase存在的意义是使用简单的快速算法过滤掉显然不会发生的碰撞,节约CPU资源。
2. Narrow Phase:对于Broadphase生成的可能的碰撞对进行精细的检测,生成具体的碰撞信息(位置,法线等等),将具体的碰撞检测结果转化为可以被solver统一处理的数据结构 (Jacobian)
3. Constraint Setup:将引擎内其它的约束(例如ragdoll关节)转化为可以被Solver统一处理的数据结构(Jacobian)
【Solve】阶段将会对生成的所有Jacobian进行统一求解,并更新物体位置、速度。
关于Contact manifolds(接触流形),后面讲破坏系统的时候将会用到,如果大家有兴趣,可以参考下面链接进行深入阅读。读者朋友可以将它直观地理解为两个物体contact信息的一种表示。
不同物理引擎之间有很多差异,但物理引擎最核心的更新步骤大都如上。基本上可以这么认为,越厉害的物理引擎,其模拟越真实、计算并行度越高,计算开销越小。
(2)破坏物运行时原理
我们以一个最简单的破碎表现为例,为大家介绍一下破坏物运行时原理。
一个长方形的板,受到撞击之后变成破碎成两个碎片。
板在破碎后,在游戏物理世界中会删除整个的板,创建两个新的板的碎片,这两个碎片的transform、linear velocity、angular velocity是根据受力点和老的板的信息而生成的。
那么这个过程是如何嵌入物理引擎的模拟过程之中的呢?
回想我们刚才绘制的那张物理引擎更新的流程图,在插入破坏系统更新之后,完整的图如上,其破坏引起更新的时机即在Collide和Solve之间,主要拆开为以下步骤:
1. 从物理模拟的collision结果里,拿到全部的contact manifold信息
2. 根据contact manifold拿到body之间的碰撞接触点
3. 计算碰撞产生的每个点的受力大小
4. 将受力直接施加到破坏体的叶碎片上
5. 根据每个碎片受力大小,计算叶节点和旁边节点的连接性并更新连接性
6. 对于碎片受力超过阈值的那些碎片,对它进行破碎,删除老的刚体,创建新的刚体(通常这个阈值是血量,后文详细讲)。
(3)可破坏物如何绘制
明白破坏物在物理世界中的呈现之后,就会迎来下一个问题,破坏物是如何被绘制到屏幕上的?
通常物理世界中刚体到渲染世界中的渲染对象是一一对应的,因此我们可以通过在物理世界中取到这个物体对应的transform,设到渲染对象上。一个破坏体及破坏体破碎之后的碎片,可以视作一系列刚体的集合。
1. 最为原始的办法,破坏物的每个碎片当成一个static mesh来绘制。在主线程中,从刚体上取得transform,设置到渲染对象上,主线程把渲染对象的状态信息,transform信息传递到渲染线程,再到RHI线程,再到shader里,最后绘制出来。但是,一个破坏体破碎之后产生了50个碎片,那50个static mesh分别单独来绘制50次,这种方式效率极差,不推荐使用。
2. 目前主流的做法都是将破碎体及碎片的集合当做一个skeleton mesh来绘制的,一个碎片对应skeleton mesh的一根骨骼,当碎片显示的时候,骨骼scale为1,当碎片不显示的时候,骨骼的scale为0。方法很简单,但是挺好用的。通常对骨骼数量几百上千的破坏体,都是这么搞的。破坏引擎的编辑工具会帮助你把原始的static mesh模型,生成破坏体的skeleton mesh模型。
3. 使用骨骼动画系统来渲染大量实时运动的物体,本质上还是一种取巧的hack。在更重视破坏系统和追求视觉效果的游戏中,往往会针对 “大量动态小碎片的渲染”这一特殊用例进行特别的开发和优化。现有的商业引擎中,UE4还处于实验阶段的Geometry Collection系统就是针对这个目的开发的。Unity方面则不太确定他们的技术路线。
(4)几何体切割后的拓扑关系
如下图,破坏体平板总共由8个碎片构成
Debris0是完整的未破碎的碎片,层级0
Debris1、Debris2是Debris0破碎后的碎片,层级1
Debris3、Debris4是Debris1破碎后的碎片,层级2
Debris5、Debris6、Debris7是Debris2破碎后的碎片,层级3
Debris3-7不可再破碎
层级结构如下图,这是一个树状结构,树状结构能很好的控制破碎后的cpu计算、渲染压力。
破坏体中Debris的叶节点,即Debris3-7,根据节点之间切割面的共面信息,可以生成一条connectivity graph(连接性图),图里包含节点信息和边的信息,其中边信息包含边长度、connectivity value(边的连接性数值,这个代表这条连接边是否容易被破坏,对于资深玩家,可以直观的理解为血量),在运行时,当connectivity value变为0时,连接断开,如果一个碎片的图的连通分量在运行时断开,那么破坏体会变成两块碎片。
通常,模型被切割成复杂破坏体之后,其connectivity graph也是很复杂的。如下图所示:
blast中对connectivity graph以及connectivity value的计算,速度还算快,但并不是非常准确,如果你对connectivity graph的正确性有较高要求,需要自己写一个根据切割面共面面积来计算connectivity graph的版本。
(5)几何体对应的刚体形状处理
在游戏引擎中,Graphics shape和physical shape往往不是一一对应的。
对物理引擎来说,基础shape类型(如box,sphere,capsule)是最省的, convex shape次之,triangle shape(mesh shape)最差,triangle shape的效率正比于其三角形数量。
一般切割出来的碎片的渲染模型和物理模型不是一一对应的,渲染模型在切面上可能会有各种参差不齐的mesh,而physical shape以convex shape居多。
下图是讲木板一分为二的例子:
首先是visual mesh。
然后是physics shape。
可以看出在切口处,visual mesh有较多的参差形状,而physics shape使用的convex hull就光滑得多。
注意这里并不能无脑把physical shape搞成convex hull,这有可能导致抖动。
(6)破坏体切割算法
在开发过程中,有较多的破坏体切分算法可选,例如有的切分更像摔碎陶瓷制品的,有的切分更像打碎水泥的,有的切分更像打坏木材的。
在这些切分算法中, 用得最多最广的是通过固定碎片数量来随机切分,为了照顾性能,我们最常给定的输入就是模型的切分数量。
这时用得最多的就是Voronoi Diagram及其变种算法。
首先简单介绍一下Voronoi Diagram,以长方形为例,假设需要将长方形随机切分成N块,那么在长方形区域中随机选取N个点,记为P1-PN。
下面由这些点来构成切割后的碎片,对每个点,它将有一个几何区域与之一一对应。
其原理是,对某个点Pi,所有到它距离最短的点构成一个几何区域。
这些几何区域之间的边界就是到Pi,Pj距离相等的点。
用这些边界分割该长方形区域,得到的图形就是voronnoi diagram,如下图所示。
通过Vonoroi 切分算法或其变种算法,我们可以方便地取得一系列分割后的破碎体形状。
除了voronoi之外,还有各种各样的算法,例如对凹几何体进行凸分解等。在实际的项目开发中,使用单一的切分算法得到的破碎物往往看起来有点不真实,让人感觉这个模型是积木搭起来的,而更真实的破碎物制作通常是多种切分算法的叠加。这里需要具体问题具体分析。
(7)Connectivity Break计算
破坏体连接图描述了静态的破坏体层次结构,破坏体切分算法产出了静态的破坏体连接图以及各个碎片的形状位置信息;那么在破坏体受力之后,可以产生出哪些对应的碎片?这就需要了解Connectivity Break计算了。
在gameplay开发中,我们触发破碎通常有两种方式,一是直接方式,即用一个dynamic 刚体与破坏体进行碰撞,撞击点和撞击力度大小都由物理引擎通过位置、速度、质量等信息来计算。二是间接方式,即人为指定破坏体的撞击点,撞击力大小。直接方式适用于大部分场景,间接方式主要适用于武侠游戏中剑气、内力的攻击,以及射击游戏中无实体子弹的射线枪攻击,和其它游戏中的爆炸伤害、激光武器伤害等等。
为了让游戏中的破坏表现更加真实,我们需要进行调参。直接方式的参数更好调节一些,设定好破坏体的connectivity值,dynamic刚体质量,速度,其效果往往不错。 间接方式的参数就比较难调了,破坏体的connectivity,撞击点位置需要通过raycast(射线检测)来查,撞击力大小需要根据经验不断尝试。
破坏体在某个撞击点,受到一定大小的力度撞击之后,它内部如何计算破碎的呢? 首先对HitPoint,变换到受击刚体的局部空间,然后搜索局部空间附近依然存在的连接,其次对这个连接进行扣血计算,并往外传导。不同的伤害类型可能有不同的传导算法、传导范围和传导系数,例如有的穿甲子弹,打中平面之后直接贯穿,受力会尽量少得传到周围,而有的达姆弹打中破坏体之后,受力会更多的传导到周围,导致到碎片掉落。
(8)破坏系统的生产工具
破坏系统的生产工具在破坏引擎里占比较大,是高产、优秀破坏系统的关键部分之一,个人认为其重要程度是大于破坏引擎runtime的。
一般情况下,纯粹用maya、max等dcc工具是无法直接产出破坏体资源的。通常美术同学会在dcc工具中产出原始的模型文件。例如一块石碑,一张八仙桌,一个书架,一根石柱,一面墙壁,等等,这些模型文件通常是static mesh。
而将static mesh文件制作成破坏体文件需要经过以下步骤:
1. 准备合适的切割面
2. 使用破坏体切分算法对破坏体进行切分
3. 按需对Connectivity手动调整
4. Debris刚体形状手动调整
5. 破坏体层级结构重建
6. 破坏体质量调整
7. 导出破坏体资源,skeleton mesh资源
导出的资源需要在你所使用的引擎(如unity、unreal、 自研引擎等)内进行测试,看看各种受击表现是否正常,跟《神海4》调ragdoll参数差不多。
在制作破坏体资产的过程中,需要反复修改测试,这个过程是比较耗费时间的,那有没有办法可以缩短时间呢?目前最好的办法,是将asset authoring集成到引擎内部,这点Unreal chaos editor集成得最好,毕竟亲生的,编辑器本身支持比较全也没啥bug,unreal blast sample 次之,做的不错,但比较老旧。有些bug,编辑工具不是非常强,例如切分算法种类不多,不能指定切割平面,不能手动调节Connectivity,碎片质量等等,但依然不失为一个好用的编辑工具。
如果你是自研引擎或者unity,推荐在引擎里实现类似功能,这种做法极大提升了破坏体制作的迭代过程,但功能开发工作量较大。除此之外,也可以做一个maya或者3ds max插件,在插件里对破坏物进行编辑、切分、导出,这种做法的开发工作量相对前者小很多,但对破坏体制作过程的迭代效率没有前者提升得高。
另外你可能不止需要编辑破坏物本身,当破坏物摆放到关卡里之后,依然有烘焙的需求,想象一下模型美术同学制作了多个不同形状的破坏物石碑,场景美术把它们摆到场景里,然后插在地里的部分要求不可被打碎,或者浮空的情况下要求破坏体底部碎片不自由落体的往下掉落,这就要求你在支持的场景里指定volume,来固定与之overlap的破坏体,在烘焙过程中,关卡中,破坏体的实例里,将volume overlap的debris打上一些标记,在破坏物/volume编辑之后提示场景信息dirty等等。
四、一些进阶的破坏效果
(1)Deform
破坏系统里的破坏体通常都是刚性的,比如前面大量讨论的受打击后碎片掉落。
也有少量需求中,破坏体受力之后形变,例如汽车车门受力之后形变,汽车引擎盖受力之后形变,油桶受力之后形变,铁板受力之后形变。
这种需求并不广泛,现有chaos、blast、apex都不能满足。那这种问题我们应如何处理呢?
如果你需要的仅仅是作假做出形变效果,推荐由美术同学提前做好几个阶段的形变后的模型+贴图,在游戏内,刚体受到一定伤害之后进行模型替换。
如果需要做出更真实一些的,运行时去计算形变后的模型渲染形状+刚体形状,你需要做到:
1. 建模阶段把可形变的破坏体拆成多个碎片,做好碎片之间的连接性,约束性,将模型导出成skeleton mesh。
2. 运行时对碎片受力进行解算,碎片之间的连接可以是做一个特殊的constraint(通常constraint进行刚体之间的连接,这里的特殊constraint对刚体内部碎片之间进行连接)。
3. 将解算器的结算结果,拿到物理引擎里,更新这个碎片的刚体形状,通常更新convex shape比较合适。
4. 将解算器的解算结果,拿到渲染里,设置成skeleton mesh每个骨骼的transform,对其进行渲染。
(2)Wood fracture
如何更真实的模拟木材破碎?
首先分析一下真实的木材破碎,木材生长都是有纹路的,切成的板材我们可以分为垂直木材纹路方向,平行木材纹路方向。这两个方向分别有不同的切口,垂直木材纹路方向通常是参差不齐的断裂口,平行木材纹路方向是比较光滑的断口。把木材切分成由这样的visual mesh构成的碎片,那么一个比较好的木材破碎效果就还差20%了。剩下的20%主要是调整参数,包括质量,connectivity,以及在木材破碎debris生成时保留约束作用。
(3)Flexible joint
这个效果主要是指由破坏加constraint构成的一个破坏体,每个碎片之间不止有connectivity连接,也有constraint连接,例如各种脚手架,都可以由这个构成。
最终一个破坏体由若干个单一的零件构成,破坏体的一部分受击之后,破坏体会处于一个“激活“状态, 整个过程会表现得摇摇欲坠的样子
这个效果很逼真,具体参见:
但这种效果使用范围很受限,因为gameplay中往往需要一个“确定“的结果,尤其是联机游戏。而Flexible joint非常严格的依赖整个物理系统的稳定更新,但即使是稳定更新的情况下,外部输入的略微改变也会导致这个系统的表现有很大不同。
比如你用视频中的办法搭建了一个架子,人可以从架子上方通过。
如果是联机游戏两个玩家都在上面架子上走,架子本身带来的不确定性会严重干扰角色移动组件。
如果是单机游戏,你可能也没有办法接受,有10%的玩家走到这里会卡bug只能读档重来。
如果是非Gameplay的部分,比如过场动画里用这个机制是比较合适的。
(4)Runtime destruction
前面讨论的都是离线破碎,一些游戏在使用运行时会破碎,典型的是彩虹六号围攻里,枪械可以形成很任意的破碎效果,比如九筒大哥在墙上打一个惊叹号。
这种效果并不能由前面提到的各种切分算法实现,需要在运行时对模型进行破碎切分,是非常先进的一项技术,联机情况下大家都能看见这个惊叹号,该技术比较复杂,在此不做分析。
五、总结
本文主要介绍现行的主流破坏系统。游戏破坏系统在绝大多数游戏中服务于游戏gameplay,是作为提升游戏表现效果的系统之一,投入适量的资源拿到期望的效果是性价比高的选择。关于如何提升游戏的破坏效果,在项目实践中,是有较多的best practice的,主要是:
1.让不同种类的破坏物,破坏效果贴近于真实,例如木材、水泥、玻璃、陶瓷、栏杆,这些东西都有不同的破坏效果。
2.如何制作减少不必要的物理更新、破坏体更新的时间消耗。
3.物理系统如何设置以减小破坏系统开销。
4.如何减少运行时的cpu spike。
5.如何解决预期之外的破坏体抖动。
6.如何做LOD。
以上内容本文没有过多阐述,读者朋友们可以结合自己的项目在这些方面进行尝试。完成这些内容,就基本上可以让你的游戏做到一个合格的破坏效果啦!
参考文献:
https://developer.nvidia.com/destruction
https://documentation.help/NVIDIA.APEX/ReleaseNotes.html
https://developer.nvidia.com/blast
https://github.com/NvPhysX/UnrealEngine/tree/Blast-4.20
https://www.havok.com/
https://docs.unrealengine.com/en-US/InteractiveExperiences/Physics/ChaosPhysics/ChaosDestruction/index.html
https://www.bilibili.com/video/BV134411y7S5
wiki https://en.wikipedia.org/wiki/Manifold
https://etnyre.math.gatech.edu/preprints/papers/phys.pdf
http://www.map.mpim-bonn.mpg.de/Contact_manifold
https://github.com/NvPhysX/UnrealEngine/blob/Blast-4.20
页: [1]
查看完整版本: 游戏破坏系统简介