让碰撞更真实 -- 天天飞车物理系统简介
本文的目标受众为刚刚进入游戏行业的同学,以及非猿类但是想要了解一下游戏研发的同学,旨在说明物理系统的一些基本概念和基本做法,高手勿喷。有错请指出,以免误导。
由于天天飞车是一个轻量级手游,其物理系统是一个比较简单的体系,没有过多的物理模拟方面的计算,当然手机上也无法支撑起大量的物理模拟计算。因此本文其实叫做“天飞碰撞系统简介”比较恰当。
游戏物理由碰撞和物理模拟两方面组成。碰撞体系主要作用在于可以使物体之间产生交互,从而使得互相独立的物体能够关联起来,产生互动;物理模拟则是通过物理计算模拟物体的运动,从而使游戏世界更加真实。一般来说,碰撞之后将产生物理反馈,但是物理模拟和碰撞之间没有必然的因果联系。没有碰撞物体也可以有物理模拟行为,比如自由落体;碰撞也不一定产生物理反馈,比如攻击用的碰撞盒,在碰到敌人之后将产生伤害。下图列举了市面上比较出名的两个物理引擎。
言归正传开始说碰撞体系。
首先说一下地形。所谓地形就是让玩家,Npc及其他一切物体的承载体,天飞中赛车在Y轴为0的平面上运动,即地形就是Y轴为0的平面。其实可以认为天飞没有地形系统,但是说了碰撞不说地形感觉有点不完整,因此稍微带过,也顺带凑点字数,当然也可以认为天飞用了一张全0的高度图(笑)。(高度图也是目前主流的简易地形实现方式,性能消耗小且有不错的表现, 以下两图展示了高度图的基本形态, 这里不多做论述, 如果需要深入了解可谷歌之)。
(图1-高度图)
(图2-高度图)
接下来说碰撞体。碰撞体是碰撞系统的核心组成部分,碰撞体之间的交互结果将成为模拟物理反馈的依据。碰撞体的形式多种多样,可以是盒子,球体,胶囊,射线,也可以是一个复杂的模型(形状越复杂其碰撞的检测难度越大,在现在cpu的运算能力基础上,还很难支撑起全面的模型级别的碰撞检测,因此一般都是使用简单的碰撞体进行模拟)。
(图-碰撞球) (图-碰撞盒)
(图-模型级别的碰撞体)
(图-射线检测)
碰撞体是游戏世界中的物体感知这个世界中的其他物体的媒介。当两个物体的碰撞盒交叠在一起了,我们就认为这两个物体发生了碰撞。也就是说,物体藉由碰撞盒的交叠,感知到了另一个物体的存在。
可以这么说,游戏中有两个世界,一个是逻辑世界,一个是物理世界。逻辑世界即我们在游戏中可见的,某车在某位置,某房子在某位置(如下图1);物理世界则是由许许多多的碰撞体组成,每一个逻辑世界中的物体,都可以有1个或多个碰撞体(不同的碰撞体可以起到不同的检测用途,如下图2)。逻辑世界是由我们的逻辑代码进行控制,而物理世界一般是由物理引擎(简单一点的话就是物理模块)进行控制,因为逻辑世界和物理世界之间没有从属关系,因此,需要每一帧将逻辑世界发生的变化同步给物理世界,以保证两个世界的变化是相同的。
比如逻辑世界中的车从(0,0,0)移动到了(100,0,0),如果不进行同步,则这辆车在物理世界中的碰撞体还停留在在(0,0,0),假设在(100,0,0)有一个障碍物的话,因为碰撞体位置的错误,车子就将穿越障碍物。
举个简单的例子说明游戏中一般的使用顺序:车子朝前方移动,首先朝移动方向做碰撞检测(1)如果无碰撞,则移动,移动结束之后同步碰撞体的信息(2)如果发生碰撞,说明路上有障碍无法移动,则进行物理反馈,如播放撞车动画等。
(图1-逻辑世界)
(图2-物理世界)
上一段中说到一个物体可以有多个碰撞体,来看一下天飞中的实际例子。下图是天飞某辆玩家车的编辑器截图,可以看到一辆车上有好几个大小不一的碰撞盒,说明了碰撞盒(物理世界)和车子(游戏中的物体,逻辑世界)没有一对一的联系,并且其位置和尺寸也可以因各种需求而改变,充满灵活性。例如下图中的红圈内的碰撞盒,是用来做超车检测用的,而黄圈内的碰撞盒是用来做捡道具用的(此处只想说明基本原理,对游戏中的具体逻辑不做过多解释),用户可以根据需求创建各种各样的碰撞体来表现游戏中物体的“存在感”。
(图-不同用途的碰撞体)
为了区分这些碰撞体,需要对他们进行分类。一般而言碰撞体上会有其类型的信息。天飞中的碰撞类型如下图。在有了类型信息之后,碰撞行为就可以更加准确,有目标性。比如玩家的Bullet类型的碰撞体只会和Enemy类型的碰撞,而不用考虑和玩家自身的碰撞体碰撞,同时也降低了碰撞检测计算的消耗。
(图-碰撞类型)
物理引擎一般会暴露出逻辑接口给逻辑世界调用,来完成碰撞操作。这些操作多种多样,用来做不同类型的碰撞检测,如rayTest, sweepTest, overlapTest等等。简单介绍一下在天飞中各种检测的用途以作示例:
1) rayTest 主要用于AI车的导航操作,即AI车向前方发射一条射线,如果这条射线碰到了其他车,则AI车感知到前方有障碍物,就会选择转向以避开障碍物。
射线检测的另一个用途:拾取(Pick)被广泛运用在各种游戏中,如FPS中鼠标瞄准,RTS中单位的选择,都是从鼠标位置发出射线,“pick”了第一个被射线“击中”的物体。
2) overlapTest 为了和SweepTest做出区别,先做介绍。如其名,这是“交叠”测试,属于静态的测试方法,即判断两个碰撞体是否发生交叠。天飞中overlap被应用在爆炸中。爆炸时产生一个大型的碰撞体以表示爆炸范围,凡是被“交叠”的,都将被爆炸席卷。
3) sweepTest正如其名,是一种“清扫”型的检测,是一种动态的检测方式。广泛用于玩家车和Npc车的各种碰撞。因为赛车的运动速度很快,一帧可能开出去几十米,此时如果使用overlapTest,则只能和运动起点以及终点的物体进行碰撞,中间过程的物体会被全部略过。“清扫”的含义就是,假设物体从A运动到B,则碰撞体也将从A开始运动到B,路上所有overlap的东西,都算被碰到。
需要注意的是,物体之间是需要相互做碰撞检测的。例如,玩家车想要检测是否碰到npc车,npc车同时也要检测是否碰到玩家车,否则有可能会因为检测时序问题产生错误的碰撞结果。
考虑下图(1)这种玩家车和npc车相向运动的情况。假设双方的速度都很大,以至于双方在一帧的运动过程中就可以超越对方的位置,如下图(2、3)。假设仅是玩家车主动发起检测,如果玩家车先动,则可以检测到npc车,如下图(2);如果npc车先动,则npc车越过玩家车,此后玩家车再运动,但是npc车已经不在玩家前方了,就错过了这次应该要发生的碰撞,如下图(3)。
(图1玩家和Npc相向运动)
(图2玩家先动,从黑色位置移动到红色位置,此时能够发生碰撞)
(图3 npc先动,从黑色位置移动到红色位置,之后玩家再动, 此时无法发生碰撞)
碰撞之后,物理引擎会返回碰撞结果,同时会返回一些碰撞信息以辅助我们做出对应的物理反馈。举个简单的例子,在天飞中,碰撞会返回碰撞盒撞击的面,藉由这个信息,我们就可以知道是车头撞到了东西,还是车侧面撞到了东西,或者是车尾撞到了东西,从而做出加速或减速或反弹的决定。更复杂的物理引擎将会返回更加丰富的碰撞信息,如碰撞的法向量,碰撞双方的距离等等,能够做出更加逼真的碰撞反馈的模拟。
(图 碰撞信息)
最后说一下物理反馈的模拟。因为天飞的轻量级游戏性,没有做很细致的物理模拟。除去基本的加速运动和跳跃之后的自由落体运动,很多物理反馈都可以使用动画来进行模拟,如被导弹击中之后车体抖动,旋转,都可以播放对应的动画来表现,这和其他游戏都是一致的。一般来说不是专门做物理模拟的游戏,都不会用复杂的物理模拟,而是用更加直观且有很好表现的动画模拟。更复杂的物理引擎会使用动量、扭矩或者弹性等更加复杂的属性来模拟物体的运动,甚至是一些流体的运动,如果水、旗帜,也是可以模拟的,这里不做详细讨论。
总体而言,物理系统是由碰撞体加物理模拟组成。每个碰撞体都有自己的类型,以增加碰撞的目标性并减少碰撞的计算量。整个模拟过程是:首先由逻辑世界和物理世界交互,发起各种各样的碰撞检测,使得逻辑世界的物理能够“感知”到周围的其他物体,并根据“感知”到的物理的类型,属性,和诸如碰撞法向量之类的碰撞信息,做出不同的物理反馈,如加减速,或者播放某个动画、声音及特效等。