共计 3203 个字符,预计需要花费 9 分钟才能阅读完成。
前言
在挑选 JavaScript 2D 物理引擎的时候,不外乎两种主流的选择:第一种是老牌的 Box2D,最开始的版本是 C ++ 实现的,后来有了很多种实现,比如 flash 版本和 js 版本,具体可看:https://stackoverflow.com/que…;第二种是新潮的 matter-js,matter-js 比较轻量,API 和文档都比较有友好。
这段时间先后折腾了 matter-js 和 Box2D,因为项目需要在微信小游戏端运行,对性能要求比较高,最终还是选择了 Box2D。
但凡涉及到这种需要经常看源码才能使用的库中文社区都非常少干货,这段时间折腾之后打算整理一些文章,分享给社区也作为一个知识备忘。
本文简单对两个引擎的性能在不同平台上进行对比,其中 Box2D 采用的是 TypeScript 实现的版本:https://github.com/flyover/bo…,作者仍然在更新,并且我看了下 CocosCreator 内置的物理引擎也是基于这个进行的封装,社区还是可以得到保证的。matter-js 采用的是 0.14.2 版本(感觉作者已经更新不动这个库了:), 大半年都不怎么活跃了)。
测试案例
在屏幕随机位置重复创建相同的矩形刚体,使之自由落体到底部,计算不同刚体数量下,全部刚体落地后每一帧的物理计算平均耗时。下面是测试中的一些截图:
影响性能的因素
机器本身的配置;
JIT:苹果端微信小游戏没有 JIT,性能会大打折扣;
刚体的随机性:刚体在随机位置生成的过程中,如果与其他刚体重叠,物理引擎需要更多的性能消耗来修正重叠,因此,每次运行测试用例数据上都不可避免会有波动。
引擎本身的设计:比如 Matter.js 没有圆形的定义,创建圆形刚体本质上是创建 25 边形,而 Box2d 天然就设计了圆形刚体,所以对于圆形刚体,两个引擎会存在不小的差异。
数据采集
因为是测试物理引擎的性能,这里不考虑 FPS,只采集物理引擎更新每一帧的时间,因为除开物理引擎,渲染引擎(PixiJS)也会带来性能消耗。
// Box2d 数据打点
let positionIterations = 3;
let velocityIterations = 8;
let timeStep = 1 / 60;
Performance.startPoint(‘box2dUpdateCost’);
world.Step(timeStep, velocityIterations, positionIterations);
Performance.endPoint(‘box2dUpdateCost’);
// Matter.js 数据打点
Performance.startPoint(‘matterUpdateCost’);
matter.Engine.update(this.engine, 1e3 / this.fps);
Performance.endPoint(‘matterUpdateCost’);
// 计算平均耗时
function calAverage(list, key) {
let sum = list.reduce((total, curr) => curr[key] + total, 0);
console.log(sum / list.length)
}
// 所有数据会收集到一个数组里面
let data = Performance.print();
//calAverage(data, ‘matterUpdateCost’);
calAverage(data, ‘box2dUpdateCost’);
Box2D 数据
机型
10 个刚体
20 个刚体
50 个刚体
100 个刚体
200 个刚体
300 个刚体
MacBook Pro 2015
0.2ms
0.4ms~0.5ms
0.6ms~0.8ms
1.3ms~1.6ms
4.6ms~5.6ms
7ms~8ms
iPhone7 Plus 微信小游戏
3.3ms~3.5ms
4.5ms~5.5ms
7.5ms~8.5ms
13ms~14ms
33ms
60ms+
OPPO R11 Plus 微信小游戏
1.5ms~2.5ms
1.8ms~3ms
3.6ms
6ms~8ms
9ms~12ms
17ms~19ms
matter-js 数据
机型
10 个刚体
20 个刚体
50 个刚体
100 个刚体
200 个刚体
300 个刚体
MacBook Pro 2015
0.5ms~0.6ms
0.6ms~1ms
2ms~3ms
3.5ms~4ms
6ms~8ms
12ms~13ms
iPhone7 Plus 微信小游戏
2.3ms~2.8ms
3.0ms~3.5ms
6.0ms~6.5ms
11.5ms~12ms
26ms~28ms
45ms
OPPO R11 Plus 微信小游戏
1.5ms~2.5ms
2.5ms
5~6ms
8ms
12ms~14ms
30ms
结论
在 PC 端,Box2d 全面战胜了 matter-js,在苹果的微信小游戏端,因为没有 JIT,Box2d 性能反而不如 matter-js,而回到安卓的微信小游戏端,因为有 JIT,Box2d 同样是可以战胜 matter-js 的。
关于圆形刚体
上面提到了两个引擎对于圆形刚体的设计,因为 matter-js 没有正统的圆形,我大胆猜测圆形刚体的性能 Box2D 会大大高于 matter-js!
特意去翻了下各自的源码,首先我们来看看 matter-js 的:
Bodies.circle = function(x, y, radius, options, maxSides) {
options = options || {};
var circle = {
label: ‘Circle Body’,
circleRadius: radius
};
// approximate circles with polygons until true circles implemented in SAT
maxSides = maxSides || 25;
var sides = Math.ceil(Math.max(10, Math.min(maxSides, radius)));
// optimisation: always use even number of sides (half the number of unique axes)
if (sides % 2 === 1)
sides += 1;
return Bodies.polygon(x, y, sides, radius, Common.extend({}, circle, options));
};
从上面的代码可得,matter-js 将 25 边形当成圆,这里在进行碰撞检测的时候,会比纯圆有更多的计算量,不知道 matter-js 作者是出于什么目的这样设计。
再来看看 Box2D 版本的实现:
class b2CircleShape extends b2Shape {
constructor(radius = 0) {
super(exports.b2ShapeType.e_circleShape, radius);
this.m_p = new b2Vec2();
}
Set(position, radius = this.m_radius) {
this.m_p.Copy(position);
this.m_radius = radius;
return this;
}
}
与 matter-js 相比,Box2D 的圆与多边形是独立的。
多说无益,我们对比下 100 个刚体状态下,两个引擎的数据对比,为了凸显差距,我们选择 Box2D 打不过 matter-js 的苹果端微信小游戏平台查看数据:
| 引擎 | 耗时 || ———— | ———— | | Box2D | 8ms | | matter-js | 25ms!! |
我们可以得出一个有意思的结论:同样是 100 个刚体,矩形刚体的耗时是 13ms~14ms,而圆形刚体的耗时下降到了 8ms,这对于一些弹球类的游戏无疑是福音,据我的观察,100 个圆形刚体在苹果端微信小游戏下面丝毫不会卡顿。而 matter-js 的耗时从 11.5ms~12ms 上升到了 25ms,显然就是在越多边形碰撞检测需要的计算量越大!