何為物理引擎,能做什麼?
物理引擎是一個計算機程式,使用質量、速度、摩擦力和空氣阻力等變數,模擬了一個近似真實的物理系統,為剛性物體賦予真實的物理效果,比如重力、旋轉和碰撞等效果,讓物體的行為表現的更加趨向真實,例如,守望先鋒的英雄在跳起時,系統所設定的重力引數就決定了他能跳多高,下落時的速度有多快,子彈的飛行軌跡等等。
四個例子不同的效果,模擬物體落入斜坡的表現
- 無物理效果
- 重力,沒有碰撞效果
- 重力和碰撞,沒有旋轉效果
- 重力、碰撞以及旋轉效果
物理引擎通常有兩種常見型別:實時物理引擎和高精度物理引擎。高精度物理引擎需要更多的處理能力來計算非常精確的物理,通常使用在科學研究(計算物理學)和動畫電影製作。實時物理引擎常用於電子遊戲並且簡化了演算法,降低精確度以減少處理時間,使得在遊戲中有更好的處理速度。
物理引擎在遊戲中的應用
CS1.5 應用的是真實度比較差的物理引擎,人物死亡後倒地動作是固定的,即使有障礙物也會執行這個動作,所以人物穿過了門。
而 CS:GO 因為採用了 Source Engine 開發,模擬較為精細,所以人物死亡之後的姿勢可以根據物理學自動計算,所以可以躺在杆上,使遊戲更加真實。
Matter.js 介紹
Matter.js 是一個用於 Web 的 JavaScript 2D 物理引擎庫,該專案誕生於 2014 年 2 月 28 號(0.5.0-alpha 版本),目前已更新迭代了 11 個版本(最新為 0.12.0 版本),它相較於老牌的 Box2D 引擎庫,Matter.js 更為輕量級(壓縮版僅有 84 KB),並且在效能和功能方面也不遜色。
在沒有 Matter.js 前,你想去製作一個物理遊戲不僅需要紮實數學知識和物理知識,並且需要通過程式語言表示出來讓機器讀懂。而有 Matter.js 就不一樣了,它為開發者提供了許多的功能模組,通過簡單易用的 API 就可以實現例如彈跳、碰撞、重力、滾動等物理效果。
Matter.js 下載
首先,需要下載開發版本或者穩定版定,並將指令碼加入到頁面中,即可開啟旅程。
1 |
<script src="matter.js" type="text/javascript">script> |
你也可以使用包管理工具 Bower 或 NPM
1 |
$ bower install matter-js |
1 |
$ npm install matter-js |
Matter.js 支援的特性
剛體 | 複合體 | 複合材料 |
凹面和凸面 | 物理特性(質量、面積、密度等) | 彈性(彈性和非彈性碰撞) |
碰撞(粗略階段、中間階段、精細階段) | 穩定的堆疊和靜止 | 動量守恆 |
摩擦力和阻力 | 事件監聽 | 約束 |
重力 | 睡眠和靜態物體 | 圓角(倒角) |
檢視(平移、縮放) | 碰撞查詢(射線追蹤、區域測試) | 時間縮放(減速、加速) |
Canvas 渲染器(支援向量和紋理) | MatterTools 工具(建立、測試和除錯) | 世界狀態序列化,需要 resurrect.js |
跨瀏覽器(Chrome、Firefox、Safari、IE8+) | 相容移動端(觸控、響應) | 原生 JS 實現 |
Matter.js 中基礎的概念
大多數的物理引擎對於物理模擬的要素都有著相近的概念,不同的引擎差別在於使用的方式,功能的全面性,模擬的精細度等層面,下面就先從物理世界的基礎概念講起。
Engine(引擎)和 World(世界)
Matter.Engine
模組包含了建立和處理引擎的方法,引擎是負責管理和更新模擬世界的控制器,引擎可以控制時間的縮放,可以檢測所有的碰撞事件,並且拿到所有碰撞的物體對(pairs)。
在 Matter.js 中任何的物體都需要一個容身處,而存放這些物體的地方,我們稱之為世界,物體必須新增到世界裡,然後由引擎執行這個世界。而建立世界需要使用到 Matter.World
模組,該模組包含了用於建立和操作世界的方法,一個 Matter.World
相當於一個複合物體,物體、約束、複合物體的聚合體,其次世界還有額外的一些屬性,比如重力、邊界。
Render(渲染)
1 2 3 4 5 6 7 8 9 10 |
// Matter.Render 用法 var engine = Engine.create(); // ... 將物體加入到世界中 var render = Render.create({ element: document.body, engine: engine, options: options }); Engine.run(engine); Render.run(render); |
element
是一個容器元素,使用時指定要渲染的節點engine
指定為Matter.Engine
例項options
指定一些渲染的引數
Matter.Render
是將例項渲染到 Canvas 中的渲染器,控制檢視層的樣式,它的主要作用是用於開發和除錯,預設情況下 Matter.Render
將只顯示物體的線框(輪廓),這對於開發和除錯很有幫助,但如果需要使用到全域性實體渲染則需要將線框模式關閉 render.options.wireframes = false
,另外它同樣也適合製作一些簡單的遊戲,因為它包括了一些繪圖選項、線框、向量、Sprite 精靈和視窗功能。
Body(剛體)
物體或者叫剛體,在物理引擎裡特指堅硬的物體,具有固定的形狀,不能形變。剛體可以用於表示一個箱子、一個球或是一塊木頭,每個物體都有自己的物理屬性,質量、速度、摩擦力、角度等,還可以設定剛體的標記。Matter.Bodies
模組中內建了幾種剛體,矩形 Matter.rectangle
、多邊形 Matter.polygon
、圓形 Matter.circle
、梯形 Matter.trapezoid
等等。
1 2 3 4 5 6 7 |
// 建立剛體 var rect = Bodies.rectangle(200, 100, 50, 50), // 矩形 circle = Bodies.circle(300, 100, 25), // 圓 polygon = Bodies.polygon(450, 100, 5, 25), // 多邊形 trapezoid = Bodies.trapezoid(590, 100, 50, 50, 3); // 梯形 // 將剛體新增到世界中 World.add(engine.world, [rect, circle, polygon, trapezoid]); |
Composite(複合體)
由剛體和複合材料通過約束組合在一起的就叫做複合體。複合體對外當作一個剛體,複合體的物理屬性是通過所包含的剛體的屬性綜合計算出來的。Matter.Composite
模組包含用於建立和處理複合體的方法,另外還有一個 Matter.Composites
模組,提供了幾種特別的複合材料,例如 鏈 Composites.chain
、牛頓擺球 Composites.newtonsCradle
、軟體 Composites.softBody
、汽車 Composites.car
、堆疊 Composites.stack
等等。
橋樑
1 2 3 4 5 6 7 8 9 10 11 |
// 使用堆疊建立橋樑 var group = Body.nextGroup(true); var bridge = Composites.stack(150, 300, 9, 1, 10, 10, function(x, y) { return Bodies.rectangle(x, y, 50, 20, { collisionFilter: { // 過濾碰撞 group: group } }); }); // 建立鏈約束 Composites.chain(bridge, 0.5, 0, -0.5, 0, { stiffness: 0.9 }); |
布
1 2 3 4 5 6 7 8 9 10 |
// 軟體 var cloth = Composites.softBody(200, 200, 20, 12, 5, 5, false, 8, { friction: 0.00001, // 摩擦力 collisionFilter: { group: Body.nextGroup(true) }, render: { visible: false } }); |
牛頓擺球
1 2 |
// 建立牛頓擺球 var newtonsCradle = Composites.newtonsCradle(300, 320, 5, 25, 150); |
Constraint(約束)
約束可理解為通過一條線,將剛體 A 和剛體 B 兩個剛體連線起來,被約束的兩個剛體由於被連線在了一起,移動就相互受到了限制。Matter.Constraint
模組包含了用於建立和處理約束的方法,這個約束可以很寬鬆,也可以很緊繃,還可以定義約束的距離,約束具有彈性,可以用來當作橡皮筋。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// 建立一個矩形和圓形 var rect = Bodies.rectangle(400, 100, 50, 50, { isStatic: true }), ball = Bodies.circle(400, 400, 50); World.add(engine.world, [ rect, ball, Constraint.create({ bodyA: rect, // 約束剛體 A pointA : { // 約束點 A x: 0, y: 0 }, bodyB: ball, // 約束剛體 B pointB: { // 約束點 B x: 0, y: -50 }, stiffness: 0.6 }) ]); |
MouseConstraint(滑鼠約束)
如果你想讓剛體與使用者之間有互動,那就要在滑鼠和剛體之間建立連線,也就是滑鼠和剛體間的約束,Matter.MouseConstraint
模組包含用於建立滑鼠約束的方法,提供通過滑鼠或觸控(移動端時)移動剛體的能力,可以設定什麼標記的物體才能被滑鼠操縱,建立滑鼠約束後,可以捕獲到滑鼠的各類事件。
1 2 3 4 5 |
// 全域性滑鼠約束 var mouseConstraint = MouseConstraint.create({ element: render.canvas }); World.add(engine.world, mouseConstraint); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 設定某個標記的物體才能被滑鼠操縱 var categoryBall = 0x0001; // 分類 var ball = Matter.Bodies.circle(300, 350, 32, { density: 0.68, // 密度 restitution: 1, // 彈性 collisionFilter: { category: categoryBall } }); var mouseConstraint = MouseConstraint.create({ element: render.canvas, collisionFilter: { mask: categoryBall } }); World.add(engine.world, mouseConstraint); |
Vector(向量)
Matter.Vector
模組包含用於建立和操縱向量的方法,向量是引擎有關幾何操作行為的基礎,修改物體的運動狀態基本都是使用向量來控制,例如賦予物體一個力,或者設定物體的速度、旋轉角度,並且內建了多個向量的求解函式:向量積、標量積、格式化、垂直向量等等。
Events(事件)
Matter.Events
模組包含了繫結、移除和觸發物件的方法。
- 繫結事件
Matter.Events.on(object, eventNames, callback)
- 移除事件
Matter.Events.off(object, eventNames, callback)
- 觸發事件
Matter.Events.trigger(object, eventNames, event)
Matter.js 中的一些屬性
施加力
Matter.Body.applyForce(body, position, force)
方法可以給剛體施加一個力,傳入 X 和 Y 軸需要的力度值,通過這個方法你可以去模擬踢一個足球、投一個籃球的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var ball = Bodies.circle(300, 100, 25, { density: 0.68, // 密度 restitution: 0.8 // 彈性 }); World.add(engine.world, ball); function addForce() { var forceMagnitude = 0.02 * ball.mass; Body.applyForce(ball, ball.position, { x : (forceMagnitude + Common.random() * forceMagnitude) * Common.choose([1, -1]), y : -forceMagnitude + Common.random() * -forceMagnitude }); } addForce(); |
重力
可以設定 X、Y 軸的重力值,預設都為 1,引數在 0、1、-1 中選擇使用。
1 2 3 4 |
// 實現反重力效果 engine.world.gravity.y = -1; // 無重力效果 engine.world.gravity.y = 0; |
睡眠狀態
通過 enableSleeping: true
開啟睡眠模式後,當剛體處於不受作用狀態時,會進入睡眠狀態,這樣可以有效的提高引擎的效能,當物體被其他物體碰撞或者對剛體施加力時,剛體會被叫醒,引擎會繼續對其進行計算模擬。
1 2 3 4 5 6 7 8 |
// 開啟睡眠狀態 var engine = Engine.create({ enableSleeping: true }); // 還可以針對進入睡眠狀態的剛體進行監聽,比如將剛體移出世界 Event.on(ball, "sleepStart", function() { World.remove(engine.world, ball); }); |
摩擦力
摩擦力在 Matter.js 中分別提供了三種:摩擦力 friction
、空氣摩擦力 frictionAir
以及靜止摩擦力 frictionStatic
。friction
預設值是 0.1,取值範圍在 0 – 1,當值為 0 意味著剛體可以摩擦力的無限滑動,1 意味著對剛體施加力後會立刻停止,frictionAir
預設值是 0.01,取值範圍 0 – 1,當值為 0 意味著剛體在空間中移動時速度永遠不會減慢,值越高時剛體在空間的移動速度越慢,frictionStatic
預設值 0.5,當值為 0 時意味著剛體幾乎是靜止的,值越高時意味著需要移動剛體所需的力就越大。
1 2 3 4 5 6 7 8 9 10 11 12 |
// 摩擦力 Bodies.rectangle(300, 70, 40, 40, { friction: 0.01 }) // 空氣摩擦力 Bodies.rectangle(300, 70, 40, 40, { frictionAir: 0.05 }) // 靜止摩擦力 Bodies.rectangle(300, 70, 40, 40, { frictionStatic: 1 }) |
時間縮放
可以控制全域性的時間,當值為 0 時為凍結模擬,值為 0.1 給出慢動作效果,值為 1.2 時給出加速效果。
1 |
engine.timing.timeScale = 0.1; |
這裡就簡單提及到幾個屬性,當然還有更多的屬性比如:檢視(View)、彈性(Restitution)等等,更詳細的 API 可到官網檢視。
Matter.js 除錯
除了前面講 Matter.Render
模組的時候提到的線框模式 wireframes
便於除錯外,Matter.Render
模組其實還為我們提供了以下幾種方法,便於我們自定義除錯選項:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
var render = Render.create({ element: document.body, engine: engine, options: { width: 800, height: 600, pixelRatio: 1, // 設定畫素比 background: '#fafafa', // 全域性渲染模式時背景色 wireframeBackground: '#222', // 線框模式時背景色 hasBounds: false, enabled: true, wireframes: true, // 線框模式 showSleeping: true, // 剛體睡眠狀態 showDebug: false, // Debug 資訊 showBroadphase: false, // 粗測階段 showBounds: false, // 剛體的界限 showVelocity: false, // 移動剛體時速度 showCollisions: false, // 剛體碰撞點 showSeparations: false, // 剛體分離 showAxes: false, // 剛體軸線 showPositions: false, // 剛體位置 showAngleIndicator: false, // 剛體轉角指示 showIds: false, // 顯示每個剛體的 ID showVertexNumbers: false, // 剛體頂點數 showConvexHulls: false, // 剛體凸包點 showInternalEdges: false, // 剛體內部邊界 showMousePosition: false // 滑鼠約束線 } }); |
另外官方提供了三個除錯工具,可單獨使用或一起使用,如下:
工具:
MatterTools.Demo
用於執行和測試 DEMOMatterTools.Gui
改變引擎的屬性MatterTools.Inspector
檢查世界
下載
- MatterTools.Demo
- MatterTools.Gui
- MatterTools.Inspector (依賴jQuery)
感謝你的閱讀。
參考資料
Matter.js – a 2D rigid body JavaScript physics engin
liabru/matter-tools
Physics engine – Wikipedia
Matter.js Demo