Cax + Matter-js 物理引擎結合使用

當耐特發表於2018-06-26

最初試過 box2dweb 與 cax框架 結合使用,發現 box2dweb 的程式碼會導致 webpack 編譯出的 cax 包模組執行順序亂套。 box2dweb 貌似也沒有官方文件和維護地址,所以棄坑轉戰 Matter-js。

Matter-js 是 Github 上最流行的 Web 2D 物理引擎,主要有如下特性:

  • 支援剛體、混合體、複合體
  • 支援凹凸多邊形剛體
  • 支援剛體間約束條件
  • 內建睡眠與靜止身體
  • 物理模擬時間加快變慢
  • 移動相容(觸控事件和PC滑鼠事件響應)
  • 物理性質(質量、面積、密度、動量守恆、摩擦阻力、重力、彈性或非碰撞檢測以及恢復等)

開始結合 Cax 和 Matter-js

引入 js

先在 HTML 引入 matter-js 和 cax, 你可以通過 npm 或 cdn 獲取 js:

準備工作

頁面新增 Canvas

<canvas id="myCanvas" width="800" height="600"></canvas>
複製程式碼

提前宣告好變數:

var Engine = Matter.Engine,
            Render = Matter.Render,
            World = Matter.World,
            Bodies = Matter.Bodies,
            Composites = Matter.Composites,
            Body = Matter.Body,
            Constraint = Matter.Constraint,
            MouseConstraint = Matter.MouseConstraint,
            Common = Matter.Common,
            Events = Matter.Events,
            Composite = Matter.Composite
複製程式碼

建立剛體

Bodies.rectangle(100, 49, 800, 44, { isStatic: true })
複製程式碼
  • 前四個引數分別代表 x y width height。需要注意的是 x 和 y 是矩形中心的座標
  • isStatic 為 true 的話代表是靜止剛體,不傳或者傳 false 為可運動剛體。

建立四面牆並新增到世界

// 建立引擎
var engine = Engine.create();

// 建立四面牆壁牆壁並新增到世界
var offset = 10;
World.add(engine.world, [
    Bodies.rectangle(400, 600 - offset, 800, offset * 2, { isStatic: true }),
    Bodies.rectangle(400, offset, 800, offset * 2, { isStatic: true }),
    Bodies.rectangle(offset, 300, offset * 2, 600, { isStatic: true }),
    Bodies.rectangle(800 - offset, 300, offset * 2, 600, { isStatic: true }),
]);
複製程式碼

建立物體並新增到世界

var stack = Composites.stack(20, 20, 6, 4, 0, 0, function (x, y) {
    if (Common.random() > 0.5) {
        return Bodies.rectangle(x, y, 64, 64, {
            bitmap: new cax.Bitmap("img/box.jpg")
        });
    } else {
        return Bodies.circle(x, y, 46, {
            desity: 0.0005,
            frictionAir: 0.06,
            friction: 0.01,
            bitmap: new cax.Bitmap("img/basketball.png"),

        });
    }
});

World.add(engine.world, stack);
複製程式碼

通過Composites.stack(xx, yy, columns, rows, columnGap, rowGap, callback)可以用來建立物體堆。 引數xx,yy分別為物體堆中第一個物體的x和y座標,columns和 rows分別為所要建立的物體堆的列數和行數,columnGap和rowGap分別為物體與物體之間的列間隙和行間隙,最後,由var body = callback(x, y, column, row, lastBody, i); 可以看出callback為生成的具體物體的方法。

看以看到,建立的時候掛在 bitmap 上去使用者後續的渲染。

發動引擎

Engine.run(engine)
複製程式碼

初始化渲染牆壁和物體

var bodies = Composite.allBodies(engine.world);

for (var i = 0; i < bodies.length; i += 1) {
    var obj = bodies[i]
    if (obj.bitmap) {
        obj.bitmap.x = obj.position.x
        obj.bitmap.y = obj.position.y
        if (obj.label === 'Circle Body') {
            obj.bitmap.scaleX = obj.bitmap.scaleY = 92 / 128
            obj.bitmap.originX = 64
            obj.bitmap.originY = 64
            obj.bitmap.rotation = obj.angle * 180 / Math.PI
        } else {
            obj.bitmap.scaleX = obj.bitmap.scaleY = 64 / 200
            obj.bitmap.originX = 100
            obj.bitmap.originY = 100
            obj.bitmap.rotation = obj.angle * 180 / Math.PI
        }
        stage.add(obj.bitmap)
    }
}

//牆壁
var topRect = new cax.Rect(800, 20, { fillStyle: '#2CB044' })
stage.add(topRect)
var bottomRect = new cax.Rect(800, 20, { fillStyle: '#2CB044' })
bottomRect.y = 600 - 20
stage.add(bottomRect)
var leftTop = new cax.Rect(20, 600, { fillStyle: '#2CB044' })
stage.add(leftTop)
var rightRect = new cax.Rect(20, 600, { fillStyle: '#2CB044' })
rightRect.x = 800 - 20
stage.add(rightRect)
複製程式碼
  • 通過 Composite.allBodies 可以拿到所以剛體
  • 通過設定 bitmap 的 scaleX 和 scaleY 可以使 剛體大小和紋理大小對應起來

更新渲染舞臺

cax.setInterval(function () {
    var bodies = Composite.allBodies(engine.world);
    for (var i = 0; i < bodies.length; i += 1) {
        var obj = bodies[i]
        if (obj.bitmap) {
            obj.bitmap.x = obj.position.x
            obj.bitmap.y = obj.position.y
            obj.bitmap.rotation = obj.angle * 180 / Math.PI
        }
    }

    stage.update()
}, 16)
複製程式碼

新增 Matter-js 內建 Debug Canvas

var render = Render.create({
    element: document.body,
    engine: engine,
    options: {
        wireframes: false
    }
});
var renderOptions = render.options;

renderOptions.wireframes = true;
Render.run(render);
複製程式碼

最終效果:

Cax + Matter-js 物理引擎結合使用

【→ 線上演示】

相關文章