cannon js 的一个简单的工作原理
1. 最高领导
把 cannon.js 想象成工厂,里面的 World 是其最高领导者。我们的代码工作原理分析,从这里着手开始。
它,管理着重力、物体列表、秩序员、鉴定员、矛盾调解员(处理冲突)、物体类型、规则手册。
分别是:this.gravity 、 this.bodies 、 this.broadphase 、 this.narrowphase 、 this.solver 、 this.materials 、 this.contactmaterials 。
World 先生,使用 World.prototype.step() 方法,每隔一段时间,比如 1/60 秒(dt),就会让工厂重新运转计算一番。
一切,从 World.prototype.step() 开始。
2. 物体
物体,就是 body 了。 它有很多属性:
- 体重 this.mass
- 位置 this.postition
- 速度 this.velocity
- 旋转 this.quaternion
- 旋转速度 this.angularVelocity
- 推力 this.force和this.torque
- 形状 this.shapes
- 类型(动态、静态、移动平台) this.type(Body.DYNAMIC、Body.STATIC、Body.KINEMATIC)
这个 body 呀,有一些日常动作:
- 添加外形 Body.prototype.addShape()
- 推力 Body.prototype.applyForce()
- 行动 Body.prototype.integrate()
第三个【行动】 ,是指在单位时间(dt)内,根据它自己的所受的到力等,精确计算自己该有的状态。其中包含了阻尼、姿态修正,以模拟摩擦力和空气阻力等,以及长时间运动造成的误差。
3. 形状
目前有四种形状(this.type):
- 弹力球 Shape.types.SPHERE
- 方块 Shape.types.BOX
- 平面 Shape.types.PLANE
- 凸多面体 Shape.types.CONVEXPOLYHEDRON
每个 body 都有一个最小外接球。 this.boundingSphereRadius 。
还有一个碰撞回调: this.collisionResponse ,它来决定这个 body 碰撞后,是否会产生反作用力。
不同的 type ,都有各自的计算方法,因为它们的集合特征不同。
4. 筛选物体
在每个时间周期里,都会进行物体碰撞筛选。具体分为两类:Broadphase 和 Narrowphase 。这两者是在筛选那些所有有可能发生碰撞的物体,它们不会考虑每个物体的形状等,只看大概的轮廓。
Broadphase 名为【宽相】,其实就是粗筛,它有两种筛选,NaiveBroadphase 和 SAPBroadphase ,前者实现很简单,但资源耗费大,是每个物体一个不落进行两两计算。后者会假设所有物体都处于同一个轴上,比如 X 轴,然后只检查相挨着的物体。这样效率大大提高。
Narrowphase 学名【窄相】,它会细致来判断两者究竟有没有碰撞,以及撞在哪里、多深、有没有摩擦力等。
【窄相】的工作流程是这样的,主要位于 Narrowphase.prototype.getContacts() 这个方法。
第一步,从【宽相】那边接过名单,然后开始分析每一对。
第二步,使用外接球来进行「初过滤」,如果外接球都碰不到,那肯定没有碰到了 ~
第三部,根据不同形状的物理规则(this.currentContactMaterial 以及该方法内部的  Material),决定碰撞时的弹力、摩擦力等。
第四步,这个【窄相】并不会具体计算各自的碰撞检测,它是一个分配员,它会根据不同的形状的碰撞模式,将任务分配给专业的工具人来处理。比如:
- 
如果是球和球,调用 Narrowphase.prototype.sphereSphere(),计算是否重叠和碰撞,并得出撞击法线(方向),以及接触点。
- 
球和方块儿,则 Narrowphase.prototype.sphereBox(),比上面那个复杂,阴为要考虑面、棱、角。
- 
平面和其他凸形状的碰撞, Narrowphase.prototype.planeConvex()。
- 
任意凸形状的碰撞, Narrowphase.prototype.convexConvex(),它最强大了,可以处理所有凸形状的碰撞。使用了 “分离轴定理” 。
第五步,当确认确实发生了一起碰撞,那么【窄相】还会写一份「纠纷报告」:
- 
ContactEquation负责碰撞报告(双方名称、双方形状、撞击点、撞击法线)。这份报告会辅助后续处理冲突(比如模型穿透了)时,物理世界需要施加多大的力才能将物体推开。
- 
而 FrictionEquation是记录摩擦的。如果发生了摩擦,则会记录摩擦力的作用方向,以便后续处理冲突,计算多大的力能阻止滑动。
第六步,资源回收。针对上面的「纠纷报告」,它其实不是每次写报告都新开一个,而是在相应的资源池 this.contactPointPool 和 this.frictionEquationPool 里拿取空报告,用完再放回去。这样将大大提高效率!
5. 处理冲突纠纷
上面的两份报告,是交由我们的领导 World 的,之后它再交给处理纠纷的 Solver。之后就是处理纠纷了!
Solver 学名求解器,它的方法 this.equations 里是各种待处理的纠纷。然后使用GSSolver.prototype.solve()(高斯-赛德尔迭代法) 来迭代处理。不是一次性处理,是迭代处理:
第一步,计算和评估每个报告的解决难度:c.computeB() (愤怒值)和 c.computeC() (抵抗力度)。
第二步,审视每个报告,再计算混乱程度 c.computeGWlambda()。
第三部,计算需要多大的力量 deltalambda,以解决这个纠纷。
第四步,保证这个力量在允许范围内(c.minForce 和 c.maxForce),防止出现 bug 。
第五步,每次计算出了那个力量,就立即通知相应的 body ,调整自己的临时速度(bi.vlambda  和  bi.wlambda)
第六步,记录下所有纠纷的 总力量变化 deltalambdaTot
第七步,分析这个【总力量变化】,如果很小,则判定当前已完成纠纷判断,从而结束纠纷处理,否则重复上述过程,继续迭代。
第八步,当纠纷处理结束,Solver 会将累计的临时速度变现,加到各自 body 的真实速度上。添加结束后,Solver 初始化,等待下一次的处理。
6. 报告的模板 Equation
Equation 学名为 方程,它就是报告的模板。每次事件后都会生成一份报告,带有一些特殊格式的信息。
上面有写过,这个报告分两种 ContactEquation (碰撞报告)和  FrictionEquation (摩擦报告),
在 solver 里,有提到过一个愤怒值 c.computeB(),它是使用每个报告都包含的核心计算公式 Equation.prototype.computeB() 计算出来的。它可以计算出其冲突发「不平衡程度」和「问题严重程度」。
7. 材质 和 两材质接触处理表
材质,即 Material , 是每个 body 都具有是属性,类似于我是木头还是橡胶。
它有 this.friction(摩擦系数) 和 this.restitution (恢复系数,又称弹性),摩擦系数 越小越光滑,而弹性,1 表示完全反弹,0 表示不反弹。
在 ContactMaterial 里定义了两种材质的互动处理方式,这里称为 两材质接触处理表 。在 World 那里,有 World.prototype.getContactMaterial() 供后续的计算查询。如果没有找到某些规则,则使用默认的规则。
8. 时间
在 cannon.js 里,dt 就是一个单位时间周期。每个时间周期,完成一个上面列出的那些计算。
World.prototype.step() 这个是计时器,每个周期开始,World 都会记录当前的时间 this.time 和 步长 this.dt 。
Body.prototype.integrate() 用于更新每个 body 的位置和旋转。它发生在 solver 在求出解后。
World.prototype.clearForces() 可以看做清零器,每个周期的末尾,都会将临时的计算值,比如临时推力 this.force 、临时扭力 this.torque 清零。确保每次计算独立,不发生 bug。
9. 其他必要组件
cannon.js 的运行原理大致就是上面第 4 小节和第 5 小节所诉,为了支持这个过程,还有很多组件作支撑:
Vec3 :三维向量计算。把数学里的向量工具代码化,能实现加 vadd、叉乘 cross、归一化 normalize (把方向标长度统一为 1)等数学运算。
Quaternion : 学名为【四元数】,表示旋转。不像 60° 90° 或 π 、 π/2 这种欧拉角复杂,更稳定。(据说可以避免“万向节死锁”)
Transform : 可以将局部坐标 转化为 世界坐标。
Mat3 : 一个矩阵计算库,3 × 3 格式的。
EventTarget : 信息事件广播工具。可以将谁睡眠了啊 谁碰撞了啊什么重要消息,广播给全局。
Pool(资源回收站)和 Vec3Pool : 资源池,将一些频繁被创建的向量、报告等统一化,重复利用化。提高代码的运行效率。
TupleDictionary、ArrayCollisionMatrix、OverlapKeeper : 各种状态记录工具,记录上一秒、这一秒的碰撞信息,辅助调解员更精准的处理纠纷,避免重复工作。
本文来自博客园,作者:独元殇,转载请注明原文链接:https://www.cnblogs.com/duyuanshang/p/18923968
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号