前言距离第一篇已经两个月了。什么,需要掌握什么样的基础知识才能继续发展。在此基础上,我们推出了第二篇探索物理引擎的学习之路。在我们日常的开发中,自然不会用到非常复杂的物理系统。大多数游戏都是以刚体为基础,然后在游戏场景中进行一些适配,最后模拟出我们常规理解中物体的运动状态。使我们感到这些位移和变形似乎是理所当然的,是有规律可循的。《愤怒的小鸟》是最著名的手机游戏之一。那么我们如何实现《愤怒的小鸟》中的效果呢?让我们一步步探索。游戏中粒子的运动首先我们来看这样一个场景。在现实中,普通子弹的速度约为300m/s,接近音速。打出去的子弹只会瞬间消失,于是射击游戏变成了玩家根本无法观察弹道轨迹的游戏,而横版游戏也变成了只能看到枪口火焰的游戏,但是弹幕是看不到的。恐怖游戏。作为“子弹”的鸟也是如此。如果真的按照在空中飞行的速度来计算的话,那么只能看到一道矫健的身影一闪而过,然后如同子弹一般穿透了大楼,飞向了天空。离这很远。这显然不是我们想要的效果。那么如何解决这样的问题呢?显然,我们应该降低游戏中高速物体的速度。一般来说,根据地图的大小,速度可以限制在5-25m/s。那么速度的问题就解决了,但是当物体的速度从300降到25时,它的能量和碰撞现象就会有很大的不同。当一只慢鸟撞到墙上时,它可能会静静地倒下,而墙会说:“什么让我痒痒的?”。那么如何解决这样的问题呢?首先,我们都知道动能、速度和质量之间的关系:$E=\frac{1}{2}mv^2$,那么当速度下降的时候,我们就需要增加质量,这三者之间的关系二是质量增加的比例等于速度下降的比例,即$\Deltam$=$\Deltas^2$。那么当质量为5g、速度为300m/s的子弹的加速度降低到25m/s时,质量应该增加144倍,变成720g。通过这种方式,我们解决了由于速度降低而导致能量减少的问题。这时,我们家的小鸟已经从一只普通的小鸡变成了强壮的猛禽,一个动作就能把一头野猪撞飞。然而,新的问题又出现了。速度降低之后是抛物线的变形。它本可以飞得更远,但因为初速度太小,落在了不远处的地面上。现在我们的肌肉猛禽又变成了大重量。如何解决这个问题呢?也就是将抛物线恢复原状。物体斜抛或平抛时,其初速度决定了抛物线的形状,其中垂直方向的高度和初速度决定了重力的作用时间,水平方向的初速度决定了抛物线的位移水平方向。当速度降低时,重力的时间和水平方向的位移都会发生相应的变化,从而产生与之前截然不同的轨迹。我们知道水平方向的位移:$x=v_{horizo??ntal}t$,垂直方向的位移:$y=v_{vertical}t-\frac{1}{2}gt^2$,其中t换成Drop,可以得到抛物线方程,$y=\frac{v_{vertical}}{v_{horizo??ntal}}x-\frac{1}{2}\frac{g}{v_{horizo??ntal}^2}x^2$,其中$\frac{v_{vertical}}{v_{horizo??ntal}}$由物体发射的角度决定,是固定值,所以影响曲线的是$\frac{g}{v_{horizo??ntal}}^2}$,由此可知重力加速度的换算公式为$g_b=\frac{g_{normal}}{\Deltas^2}$,但事实是否如此?不是,因为当我们降低速度的时候,我们的场景实际上是在缩小,原来300米的距离同时缩短到25米,所以y和x需要有相同的缩放比例才能得到相同形状的抛物线。那么我们可以得出结论,真正的重力加速度换算公式应该是$g_b=\frac{g_{normal}}{\Deltas}$(这里省略了一些联立方程)。游戏中的合力蓄能器接触过简单力学的人都知道,当多个力作用在一个物体上时,我们可以利用合力和分力将多个力的复杂组合处理成我们方便的东西计算。几个方向的力量。首先我们要明确,合力是多个力在向量层面的叠加,所以需要用到向量的加减法。最常用的是平行四边形法则,平行四边形法则在坐标轴上的处理很简单,只需将两个向量的坐标相加即可。但力本身不一定是恒定的。例如,空气阻力可能会随着速度的增加而增加;浮力随着进入水中的体积的增加而增加。所以我们需要时刻计算合力,所以在游戏中,每帧计算一次。我们可以通过创建一个专用于联合力的类来做到这一点,称为蓄能器。累加器的概念在面向对象编程和函数式编程中使用较多,在js中也有相应的操作方法——Array.reduce。它可以直接累加数组中的元素。这里的累加不只是累加的意思,可以是任何操作。那么事情就简单了,我们只需要将力汇总在一个数组中,给它一个累加函数就可以了。我们以二维平面为例:classForceAccum(){constructor(){//初始化力数组this.forceArray=[]}addForce(force){//给数组加上力this.forceArray.push(force)}clearForceAccum(){//为下一次计算清空数组this.forceArray=[]}forceReducer(prevForce,currentForce){//计算合力constx=prevForce.x+currentForce.xconsty=prevForce。y+currentForce.yreturn{x,y}}getForce(){//获取合力constforce=this.forceArray.reduce(forceReducer)returnforce}}constforceAccum=newForceAccum()//创建一个新的力累加器forrceAccum.addForce(gravity)//增加重力forrceAccum.addForce(resistance)//增加阻力forrceAccum.addForce(thrust)//增加推力constforce=forrceAccum.getForce()//计算合力forrceAccum.clearForceAccum()//cleararray接下来的计算,上面就是最简单的蓄力器及其应用,给系统加力,最后得到合力。得到合力后,我们可以根据牛氏第二定律得到加速度,最后根据加速度积分得到速度,再根据速度积分得到位置。然后我们每一帧重复上面的操作。而我们的内在《愤怒的小鸟》需要什么样的力量?可以看出,需要的是弹弓的弹力和自身重力产生的推力,那么我们只需要将这两个力加到我们的蓄力器中,就可以计算出我们的肌肉猛禽及其水平和垂直受力军队。我们在实际练习中,只有一个重力在起作用(不考虑空气阻力),推力在弹弓最终在弹性变形中转化为飞行初速度。因此,为简化计算,可将弹簧的推力直接换算成初速度。当然也可以用冲量和动量来计算。那么我们应该如何获取和计算代码中的每一个力呢?这需要使用另一种东西——力发生器。力发生器力发生器,顾名思义,是创造力的装置。因为我们在运动中,有各种各样的力,有的力是不变的,比如质量不变时的引力;有些力是根据场景而变化的,比如速度不断变化时的空气阻力;还有一些力是根据玩家的操作而变化的,比如推力、弹力等。那么其实我们可以根据这些力的特点为它们注册对应的力发生器,这样我们就可以更好的管理它们。力发生器的原理很简单,我们通过一个类来创建它。但是,每一种力量的特点是不同的,所以我们需要应对不同的力量。这里有两种不同的方法。一种是把所有需要包含的力都写在一个类中。当需要创建力时,使用相应的方法;另一种是将力的产生方法和参数传入一个类中,最后返回需要的力,或者直接将力注入力累加器。后一种灵活性将使我们能够更好地控制复杂机械系统中的系统。classForceRegister(){constructor(forceAccum,func,param){this.forceAccum=forceAccumthis.func=functhis.param=paramthis.force=null}createForce(){//返回所需力this.force=this.func(this.param)returnthis.force}addForce(){//直接给力累加器注入力this.createForce()forrceAccum.addForce(this.force)}}上面代码中的func和param是什么我们需要生成力方法和参数。比如重力,重力只需要输入物体的质量(或者质量的倒数)和重力加速度,就可以得到对应的力-{x:0,y:mg}。阻力也是如此,阻力方程为$\displaystyleF_{D}\,=\,{\tfrac{1}{2}}\,\rho\,v^{2}\,C_{D}\,一个$。参数先不赘述,化简得到$F_{D}=av^2$,a是一定条件下的参数。在这种情况下,我们阻力发生器的参数是a和速度v,然后方向就是运动速度的反方向。借助力发生器和蓄力器,我们可以轻松地管理游戏中的机械系统。但是游戏中每一帧都需要大量的计算和刷新,对性能的要求肯定是比较高的。于是就有了各种优化方法。例如,我们可以去掉空气阻力、水流阻力等,以节省复杂的计算,或者只给出一个固定值进行计算。对于更重要的引力,我们可以通过内置的引力,直接将引力的加速度存储到整个物理系统中,而不用在每个物体的每一次计算中都包含引力。所以其实我们理解为只需要在系统中设置重力加速度,根据用户的操作设置初速度的向量,就可以快速完成小鸟的抛物线运动。总结通过简单的力学知识和适当的代码,我们可以创建一个符合力学定律的超简单的《愤怒的小鸟》世界版本。但这实际上是远远不够的。游戏中除了力的简单叠加和位移,还有力矩、碰撞、旋转、角速度等。只有将这些相加,我们才能计算出碰撞、得分,并合理地执行物体被击中后的受力、旋转和运动。这些有趣的内容,敬请期待我们物理引擎系列的后续章节~欢迎关注傲兔实验室博客:aotu.io或关注傲兔实验室公众号(傲途实验室),不定期推送文章。
