《游戏引擎架构》第12章 碰撞及刚体力学——学习笔记
第一次写学习笔记,以前也没有这样的习惯。现在想了想还是得留下写记录,可以巩固自己学过的东西,怎么也没有坏处。这本《游戏引擎架构》我没有从头看,应该是个人习惯问题,很多时候想看哪就直接看哪。这次看一下碰撞及刚体力学的章节。虽然没有打算开发引擎(也没有那个水平),但是多了解这些对于日常Unity的使用肯定还是有帮助的,顺便做个笔记。
总结
还没想好
碰撞及刚体力学——章节简要内容
12.1你想在游戏中加入物理吗
12.1.1物理系统可以做的事情
- 检测碰撞
- 引力作用
- 布娃娃系统
- 布料模拟
- 悬挂的物体
- 触发器
- 水面浮力
- 等等等等……
一是可以实时的运行物理模拟,二是可以提前通过物理模拟生成动画片段。
12.1.2物理好玩吗
游戏中使用刚体动力学系统不一定能让游戏变得好玩,有时候甚至会相反。
在模拟类、物理解密类游戏、沙盒游戏中,物理系统起很重要的作用。不过有时候会为了提高游戏性而降低物理系统的真实性。比如更夸张的爆炸效果、更低的引力作用。过于真实可能会影响游戏进程,比如爆炸碎片把本来要通过的道路给堵住了。
有的游戏就不需要物理系统(比如扫雷),或者有的游戏看似需要物理系统其实也可以不用。
12.1.3物理对游戏的影响
如果需要确保每一次的运行结果都一样最好使用动画而不是物理模拟,因为实时模拟受到很多因素的影响,很难确保每次都一样。比如在关卡设计中需要楼房倒塌把道路堵住,然后寻找其他的道路出发剧情。如果是基于实时物理的模拟,可能某一次倒塌就没有把道路给堵住,影响了游戏的继续。
物理模拟有意外性,有时候也会让游戏多出一些意料之外的玩法和乐趣。
对AI的影响,物理模拟可能会使场景发生不可预期的改变。可能就使得预先烘焙好的寻路系统不能正常工作,动态的处理这些变化。
布娃娃物理,在播放动画时身体各个部分的碰撞体有可能重叠。此时切换到布娃娃很可能会造成极大的不稳定性。需要采取措施避免。
网络多人方面,不影响到游戏性的物理效果可以在本地独立模拟,影响到游戏性的物理(如手榴弹的轨迹)需要在服务器上模拟,并准确地复制到所有客户端。
12.2碰撞/物理中间件
实现物理系统是富有挑战性和耗时的工作,可以使用一些商业或者开源的物理SDK。
- I-Collider、SWIFT、V-Collide、RAPID 官网 (http://gamma.cs.unc.edu/I-COLLIDE/把unc看成nuc了…还以为是我们学校的网站),开源
- ODE SDK,开源免费
- Bullet SDK,开源
- TrueAxis 官网,非商业使用下免费
- PhysX PhysX-SDK,免费提供CPU版的SDK,付费获取源码
- Havok 官网,付费 (好像这个特别厉害,而且Havok宣布和Unreal、Unity合作,不知道用上了没有)
- PAL 官网,开源
- DMM 官网,好像不是开源的(百度搜出来一个情趣用品品牌…)
12.3碰撞检测系统
碰撞系统主要用途在于判断物体是否接触,这些物体通常是球体、长方体、胶囊等简单的形状(也可以是复杂的形状)。碰撞检测系统本质上是集合相交测试器。
相互穿插
除了碰撞判断,还有很多物理行为,比如反弹、滚动、滑动、支撑等。
12.3.1可碰撞实体
对象之间的碰撞需要一个碰撞表达形式。碰撞表达形式是一个独特的数据结构,分离与对象的游戏性表达形式及视觉表达形式(网格、粒子效果或其他表达形式),这个概念在Unity中是碰撞体(Collider)。碰撞体通常只会用一个与模型相近的一个简单形状表示,用于减少运算的压力。
|
|
可碰撞体包含两个基本信息,形状 和 变换。形状描述可碰撞体的几何外形,变换描述形状在游戏中的位置和定向。(这个“变换”不太理解,可能原文是Transform)
碰撞物需要变换的三个原因:
- 形状只描述物体的外形、尺寸,形状通常以原点的中心来定义。形状必须被变换,使其合适的放置及转向于世界空间中。
- 游戏中许多对象是动态的。如果把形状的特征(顶点、平面等)逐一移动,会很耗时。使用变换的话,无论形状的特征是简单还是复杂,都能快速的移动。(不清楚为何变换可以快速移动)
- 多个碰撞体共享一个形状描述可以节省内存空间。如赛车游戏中多种车的形状信息可以是相同的。
12.3.2碰撞/物理世界
碰撞系统通常会通过一个名为碰撞世界的数据结构管理所有可碰撞体。把所有碰撞信息维护于私有的数据结构的优点有,碰撞系统无需遍历无关的数据结构,高效的组织碰撞数据。
12.3.3关于形状的概念
形状是一个由边界所指明的空间区域,能清楚界定形状的内外,
有的对象比如地形、河流最好用表面来表示,表面有前后之分,无内外之分。
碰撞库通常会有一个挤压参数来表示表面有多厚,避免高速细小的物体穿过表面没有碰撞的情况。
|
相交:两个形状的交集仅仅是同时位于两个形状中的所有点(无穷大)的集合。
分离矢量:沿着这个矢量移动,就能高效的脱离碰撞状态。
凸性:碰撞检测中重要的概念之一就是判断凸和非凸,由形状内发出的光线不会穿越形状表面两次或以上就是凸。(就是算法中的凸包问题)凸形状一般会比凹形状需要较少的运算。(在Unity中,MeshCollider就有一个Convex选项把碰撞体的形状转为凸包的)
12.3.4碰撞原形
碰撞检测系统一般只支持有限的形状类型,称为碰撞原形。
- 球体
球体(Sphere)是最简单的三维体积,是最高效的碰撞原形。以球心和半径表示,可以很方便的包裹在一个四元浮点数矢量中,在SIMD数学库中运作的特别好 - 胶囊体
胶囊体(Capsule)是把一个球从A点线性移动到B点所勾勒的形状,通常由两点和半径表示。计算胶囊体的相交比圆柱体和长方体高效(可能这就是为什么Unity中连圆柱体的碰撞体都是胶囊)。 - 轴对齐包围盒
轴对齐包围盒(Axis-aligned bounding box, AABB)是一个矩形体积。可以简单的由两个点定义(这里的意思应该是定义一个二维的碰撞体,三维上应该需要3个点。更正一下,后来想了想其实只要对角的两个点就可以确定一个三维的矩形了,不需要三个点)。可以高速的测试与另一个AABB是否相交,但只适用于形状逼近长方体且主轴大约于坐标系统的轴对齐的物体。(这么看来应该适合大部分只有一个平面上的移动游戏) - 定向包围盒
定向包围盒(Oriented bounding box, OBB)通常用三个“半尺寸”(半长、半宽、半高,相当于缩放)和一个变换表示。一种常用的碰撞原形(猜测,就是Unity中的BoxCollider?),能较好地切合任意定向的物体,表示方式仍算简单。 - 离散定向多胞体
离散定向多胞体(Discrete oriented polytope, DOP)是一种凸的胞形,k-DOP是有k个平面构成的形状,AABB就是一个6-DOP。 - 任意凸体积
本质上是k-DOP,凸体积的相交测试比上面的简单几何原形都要更耗时。 - 多边形汤(对,就是叫汤)
多边形汤(Polygon soup/poly soup)是完全任意、非凸的形状,常用于复杂的静态集合建模,例如地形、建筑物。是最费时的碰撞检测,通常把多边形汤应用在不参与动力学模拟的物体。可以是个开放的表面,不一定有体积。 - 复合形状
复合形状(Compound shape)经常作为非凸物体建模时的高效替代品。一些碰撞系统在碰撞测试时,会把符合性状的凸包围体积作为整体,如果两个凸包没有相交则无需测试子形状的碰撞,如果相交再测试子形状是否相交。
12.3.5碰撞检测及解析几何
- 点与球体的相交
很简单,计算点到球心的距离和半径的关系就可以了 - 球体与球体的相交
也很简单,计算两个球心的距离和半径的关系。 - 分离轴定理
分离轴定理(separating axis theorem)为,若找到一个轴,两个凸形状于该轴上的投影不重叠,就能确定两个形状不相交。若这样的轴不存在,并且那些形状都是凸的,则可以确定两个形状相交。
三维上分离轴也是一个轴(只是方向,不过特定点)。
一篇讲OBB包围盒的博客,用到分离轴定理。 - AABB与AABB相交
也可以使用分离轴定理,若存在分离轴则必然是三个坐标轴之一(因为跟轴是对齐的)。 - 检测图碰撞:GJK算法
一个非常高效的算法,可检测任意凸多胞形(看不懂,以后有空再看看相关文献把)。 - 其他形状对形状组合
对于N种形状,所需的成对测试需要O(N2)个。碰撞引擎的复杂度很大程度上是由大量所需处理的相交组合造成的。这也是GJK流行的原因,GJK能过处理所有凸形状之间的碰撞检测。不同形状的唯一区别只在于算法所使用得支持函数。可以有双分派和单分派的方法选择合适的碰撞函数。 - 检测运动物体之间的碰撞
以上都是静止物体的相交测试,在运动的物体中,最简单的方法就是在每个时间步中将每个刚体的位置和定向当作精致,然后静态相交。但是在有细小高速的物体时就容易出现“子弹穿纸/遂穿”的情况,就是两个时间步之间的空隙正好错过碰撞。下面是常见的解决方法。- 扫掠形状
在两个时间步之间做线性插值,可以确保不错过碰撞。但是如果路径是曲线或者物体在旋转,线性插值之后碰撞的结果不一定准确。还有就是曲线产生的非凸体积也会耗费更多的运算。 - 连续碰撞检测
连续碰撞检测(continuous collision detection),对于两个移动物体于某区间内,求得最早得冲击时间(time of impact,TOI)。
- 扫掠形状
12.3.6性能优化
通常使用空间散列(spatial hashing)、空间细分(spatial subdivision)、层次式包围体积(hierarchical bouding volume)的方法降低所需的相交测试次数。(这些技术感觉还是挺有用的,不止用在碰撞上)
利用时间一致性。(没懂)
使用八叉树、BSP、kd树、球体树等把空间分开。
分阶段的碰撞检测,如先用AABB测出有机会碰撞的物体(一般使用扫掠裁减(sweep and prune)算法),再继续测复合体积的包围体,最后再测试各个原形是否相交。这样可以不必对每个体积都细致的测试。
12.3.7碰撞查询
查询一个范围内的物体或者射线之类的。并没有实体在碰撞世界中,所以不会影响到世界中的物体。
- 光线投射
用一个起点和矢量(包括距离)描述光线,返回第一个接触的或者所有接触点的信息。 - 形状投射
将一个凸形状沿一条有向线段移动多远会碰到其他物体。定义起点、矢量(包括距离)和形状的信息。返回触点的信息。 - Phantorm
与普通碰撞体类似,就是不参与任何动力学模拟。(类似Unity中OverlapXXX的概念吧)
12.3.8碰撞过滤
需要启用、禁用对某类型物体的碰撞检测,根据游戏的具体准则来决定碰撞体之间接触是否成立,这就是碰撞过滤。
- 碰撞掩码及碰撞层
对物体进行分类,给每一个层(类)一个掩码,用来确定是否能与其它层碰撞。(Unity中射线就可以通过Layer来过滤) - 碰撞回调
检测到碰撞时调用回调函数,检查碰撞的具体信息决定是否接受碰撞。 - 游戏专门的碰撞材质
给物体进行分类,关联上材质属性,定义了某种表面在物理上的碰撞行为。典型的做法是碰撞原形以8、16、32位整数制定碰撞材质。此整数用于索引至某数据结构数组,内涵详细的碰撞属性。
(终于把12.3弄完了。。。太累了,后面刚体力学还有很多内容,慢慢来吧)