JavaScript物理引擎之Matter.js與Box2d性能對比


前言

在挑選JavaScript 2D物理引擎的時候,不外乎兩種主流的選擇:第一種是老牌的Box2D,最開始的版本是C++實現的,后來有了很多種實現,比如flash版本和js版本,具體可看:https://stackoverflow.com/questions/7628078/which-box2d-javascript-library-should-i-use;第二種是新潮的matter-js,matter-js比較輕量,API和文檔都比較有友好。

這段時間先后折騰了matter-js和Box2D,因為項目需要在微信小游戲端運行,對性能要求比較高,最終還是選擇了Box2D。

但凡涉及到這種需要經常看源碼才能使用的庫中文社區都非常少干貨,這段時間折騰之后打算整理一些文章,分享給社區也作為一個知識備忘。

本文簡單對兩個引擎的性能在不同平台上進行對比,其中Box2D采用的是TypeScript實現的版本:https://github.com/flyover/box2d.ts, 作者仍然在更新,並且我看了下CocosCreator內置的物理引擎也是基於這個進行的封裝,社區還是可以得到保證的。matter-js采用的是0.14.2版本(感覺作者已經更新不動這個庫了:),大半年都不怎么活躍了)。

測試案例

在屏幕隨機位置重復創建相同的矩形剛體,使之自由落體到底部,計算不同剛體數量下,全部剛體落地后每一幀的物理計算平均耗時。下面是測試中的一些截圖:

影響性能的因素

  1. 機器本身的配置;
  2. JIT:蘋果端微信小游戲沒有JIT,性能會大打折扣;
  3. 剛體的隨機性:剛體在隨機位置生成的過程中,如果與其他剛體重疊,物理引擎需要更多的性能消耗來修正重疊,因此,每次運行測試用例數據上都不可避免會有波動。
  4. 引擎本身的設計:比如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.6ms1ms|2ms3ms|3.5ms4ms|6ms8ms|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個剛體,矩形剛體的耗時是13ms14ms,而圓形剛體的耗時下降到了8ms,這對於一些彈球類的游戲無疑是福音,據我的觀察,100個圓形剛體在蘋果端微信小游戲下面絲毫不會卡頓。而matter-js的耗時從11.5ms12ms上升到了25ms,顯然就是在越多邊形碰撞檢測需要的計算量越大!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM