物理引擎box2d解析
因为游戏开发中物理引擎是非常重要的模块,实际上用到的物理引擎如PhysX太过庞大,研究源码是一件非常费力的事情,所以从简单的物理引擎源码入手。
背景
对于计算机物理来说,最为简单的和基本的流程是碰撞检测。其中碰撞检测技术最为直接的碰撞检测查询是冲突检测和相交测试。
可以先从2D的物理引擎来说起,Box2D是二维的高效碰撞检测的引擎,其已经商业用在了经典的游戏《愤怒的小鸟》这一款游戏上,其出色的碰撞检测能力和物理模拟能力也得到了广大游戏玩家的认可。
box2d-lite预览
先从Erin Catto给的最为简单的box2d-lite这个项目看起,整体的实现框架的核心是找到两两刚体发生了碰撞的,并根据碰撞信息模拟出碰撞反馈结果即可。
框架分为两个部分broad phase和narrow phase,分别是粗略判断是否产生碰撞和精确判断两两刚体的碰撞关系和模拟碰撞过程。
box2d-lite简要代码
整个代码只包含了Arbiter,Body,Joint和World四个类型。
其中World作为模拟物理世界中的载体,承载所有的Body和Joint。整个世界的更新tick逻辑也在里面;在tick的过程中是先进行broadPhase阶段,第二步骤是计算各刚体收到外力对其速度的影响,施加在速度上,第三步骤是计算各Arbiter的碰撞点的受力和反馈情况,第四步骤是根据Arbiter的碰撞点反馈情况给各刚体进行速度上的反馈,第五步骤则是根据各个刚体的速度来计算新的位置。
其中Body特定简化为2D平台的正方形,边长为width,具备的属性有position位置,rotation旋转角度,velocity移动速度,angularvelocity角速度,force外力,torque扭矩,friction摩擦力,mass质量,invMass质量的倒数,I和其倒数
其中Joint是管理连接两个Body的类,记录了两刚体body的挂接点,偏置信息,是否软链接等
其中,Arbiter作为发生碰撞的两个body的模拟碰撞过程的承载类,会记录两个body的指针,两者的摩擦力,碰撞点信息。其中碰撞点信息记录了两个刚体Body的碰撞特征。
从world的主循环来进入,首先会进行broadPhase,把发生碰撞关系的两两body,加入Arbiter管理的哈希表中,如果是新元素则加入哈希表,否则更新该Arbiter对象。
其中刚体判断是否碰撞的关键代码在Collide.cpp里,包含了检测两个正方形是否碰撞的物理计算。
项目box2d-lite是最为简单的版本,从中可以看到物理模拟的最为简单直接的实现。
那么进入正式项目box2d的探索
box2d代码
项目box2d增加了很多新的特性
对象缓存池:BlockAllocator模拟的是申请堆内存,StackAllocator模拟的是申请栈操作,BlockAllocator会实现申请一块大的内存,并把内存划分为不同尺寸的内存对象,根据Allocate接口来进行按需分配,类似伙伴算法的操作。StackAllocator是划定一块内存以栈的形式来分配,分配和回收必须是对应栈的先进后出的逻辑。
世界管理,world里面还是管理所有的物体,以step为循环
也多了很多的特性,如软绳rope,
性能检测profile模块,主要记录的是各个子模块的调用频率,可以让使用者定位主要的开销和花费的来源。
优化内容
优化的主要是如下几个方面
丰富了物体库,支持多种多样的刚体库
在空间管理上增加了动态树的结构来管理刚体对象,优化了broadPhase的开销
增加了调试DebugDraw和Profile的工具,方便开发者进行调优
参考
box2d-lite源码:https://github.com/erincatto/box2d-lite
box2d源码:https://github.com/erincatto/box2d