HTML5之2D物理引擎 Box2D for javascript Games 系列 第一部分

池中物willian發表於2016-05-10

 

我要的是能在H5頁面上跑的javascript版的Box2D啊!!!

最近想學習Javascript版本的Box2D JS物理引擎,無奈搜了半天也沒找到相對比較系統的資料

官方網站也只是簡單的介紹,API還引導向了FLASH AS3指令碼。

 

我要的是能在H5頁面上跑的javascript版本的教程啊!!!

 

後來搜出了一本中文版Box2D for Flash Games,指令碼是AS3版本的書。是由天地會(暱稱:魯邦三世)翻譯的

看,書封面

 

沒有Javascript版本的啊。點解?(υ◉ω◉υ)

 

So... 我感覺上帝選中了我⋋(◍’Θ’◍)⋌

以前的我是AS3指令碼程式猿出生, 那麼唯有用我丟掉的幾年AS3指令碼經驗把它改寫成Javascript版本的了,誰讓我現在寫的是Javascript呢。

看…我做的封面

 

完美!!!

用Fireworks把原來的封面做成了javascript,可見我功力了吧,請叫我美工殿下!

我最早是做美工出生我會跟你說?(/ ̄(エ) ̄)/

 

好吧,那我就一邊學,一邊改成javascript版本了。

如果文章有侵權行為,那麼,要麼聯絡我刪掉?

要麼來告我,反正我也沒錢(ミ ̄ー ̄ミ)

我的郵箱willian12345@126.com

 

本系列原始碼持續更新中,已寄存在github上

https://github.com/willian12345/Box2D-for-Javascript-Games

 

開始前的一些說明 

你必定假設你對javascript或者前端知識已經比較熟悉了,如果不熟悉的話你得先補一下前端知識再往下看

FLASH中的舞臺對應網頁中的Canvas

AS3 (ActionScript3.0)指令碼對應網頁中的Javascript

 

 

2D物理引擎中的一些概念名詞翻譯列表

rigid body :剛體


fixture :夾具


box :盒子或矩形

debug draw : 除錯繪圖

density :密度


friction :摩擦或摩擦係數

restitution : 恢復或恢復係數

force : 力或作用力


impulse : 衝量


linear velocity : 線速度或線速率

joint : 關節


motor : 馬達


bullet : 子彈


sensor : 感應器

 

 

目錄


 

 

 

第一章 Hello Box2D World

 

定義Box2D世界

執行模擬

概述

 

第二章 向世界新增剛體

 

你的第一個模擬----一個球落地

建立一個圓形形形狀

建立夾具

使用除錯繪製測試你的模擬

建立矩形形狀

不同的剛體型別----static, dynamic 和 kinematic

密度,摩擦和恢復

建立圖騰破壞者的關卡

建立複合剛體

建立定向矩形

建立各種型別的凸多邊形

概述

 

第三章 剛體的互動

 

通過滑鼠點選選擇並銷燬剛體

將自定義屬性指定到剛體上

遍歷剛體並獲取它的屬性

概述

 

第四章 將力作用到剛體上

 

蘋果掉落,修正

力,衝量和線速率

應用衝量來得到線速度

應用力來獲得線速度

將力應用到真實的遊戲中

物理遊戲不只是關於物理

放置物理小鳥

發射物理小鳥

概述

 

第五章 碰撞處理

 

碰撞檢查

Box2D內建的碰撞監聽

將碰撞開始和結束輸出到輸出視窗

檢測當你要解決碰撞和當你解決了碰撞

在圖騰破壞者中檢測神像墜落地面

在憤怒的小鳥中銷燬磚塊並消滅小豬

概述

 

第六章 關節和馬達

 

拾取並拖拽剛體—滑鼠關節

讓剛體之間保持給定的距離—距離關節

使剛體繞一個點旋轉—旋轉關節

當憤怒的小鳥遇見粉碎城堡

通過馬達控制關節

通過鍵盤控制馬達

讓一些剛體不要發生碰撞—碰撞過濾

將它們放在一起

概述

 

第七章

使用你自己的影象資源代替除錯繪圖

概述

 

第八章 子彈和感測器

 

感受隧道效應

阻止隧道效應—設定剛體為子彈

通過感測器檢測接觸,可以允許剛體重疊

 

第一章

1、Hello Box2D World

 


 

如果你想建立2D的物理驅動遊戲與應用,Box2D是最佳的有效選擇。

Box2D是一個 2D剛體的模擬庫,它被使用在一些最成功的遊戲上,例如在iPhone上的Angry Birds 和Tiny Wings或者在Flash上的Totem Destroyer和Red Remover。

Google一下它們,你 將會發現很多熱心的評論。

在我們進入Box2D世界之前,讓我說明一下什麼是剛體(Rigid Bodies)。

它是一塊非 常堅硬的物質,任何方法都不能使它彎曲。無論你怎樣用力去撞擊(Hit)或投擲 (Throw)它,都無法改變它的形狀。

在真實世界中,你可以將它的硬度想象成鑽 石,甚至比鑽石還要硬。也許你可以展開想象,假設它是來至外空間的一塊不可變形 的物質。

Box2D只管理剛體(Rigid Bodies),從現在起,我們將稱它為剛體(Bodies)(這句 話中文是看不出什麼不同的,英文將Rigid Bodies簡稱為Bodies),不用當心,你將還 可以模仿不是剛性的材料,例如彈性球體。

讓我們看看,你將在本章節學習到那些知識:

• 安裝Box2D


• 建立你的第一個Box2D世界


• 瞭解重力和睡眠剛體

• 執行程式,操作時間步和約束

本章結束後,你將能建立一個空的可執行的世界,在那裡你可以搭建你那了不起的物理遊戲。

 

 

在網頁中安裝Box2D


 

你可以從官方站點下載最新的Box2D.js版本或在我的github上直接下載合併好的原始碼 https://github.com/willian12345/Box2D-for-Javascript-Games/

 

新建一個demo1-1.html頁面

下載到Box2D.js版本後放在網頁同級目錄或其它目錄

根據目錄在網頁中直接引用

<script src="Box2d.js"></script>

可比在比FLASH簡單多了

網頁中再新增

<script type="text/javascript">

            function init(){

            function main(){

               console.log(Box2D);     

            }

 

            main();

         }

init();

</script>

 

測試一把

 

在瀏覽器中開啟1.html

開啟瀏覽器除錯工具,推薦用chrome瀏覽器。

應該能看到偵錯程式的Console內輸出Object {Collision: Object, Common: Object, Dynamics: Object}

 

init()方法可以在onload時呼叫, init方法內再建一個main方法

模擬FLASH中自動呼叫的Main.as類。這裡需要手動呼叫。當然你也隨便建方法

 

完整程式碼在demo1-1.html中

 

第一步成功!

為了方便,在init方法一開始內可以新增以下程式碼,方便物件的使用

var b2Vec2 = Box2D.Common.Math.b2Vec2

   ,b2AABB = Box2D.Collision.b2AABB

   ,b2BodyDef = Box2D.Dynamics.b2BodyDef

   ,b2Body = Box2D.Dynamics.b2Body

   ,b2FixtureDef = Box2D.Dynamics.b2FixtureDef

   ,b2Fixture = Box2D.Dynamics.b2Fixture

   ,b2World = Box2D.Dynamics.b2World

   ,b2MassData = Box2D.Collision.Shapes.b2MassData

   ,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape

   ,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape

   ,b2DebugDraw = Box2D.Dynamics.b2DebugDraw

   ,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef

   ;

 

先不用管具體做什麼用,後面會一一用到的

 

當我給本節標題命名為Hello Box2D World時,我並不只是為了建立另一個"Hello World"程式,而是我想要介紹所有Box2D模擬和事件發生的環境:世界(World)。

世界(World)是模擬發生的舞臺。你想要通過Box2D物理引擎控制的所有事物必須

在世界(World)中。幸運的是,Box2D世界(World)擁有足夠大的空間來容納你 需要的任何事物,所以你無需擔心世界(World)的邊界(boundaries)。

你只需要 記住在電腦中的任何事物都要受到某種限制。所以,越大的世界(World),將會 消耗你的電腦越多的資源去管理它。

定義Box2D世界


與現實世界一樣,Box2D世界(World)有重力(gravity),所以你需要先定義世界 重力(world gravity)。

1. 在你的main方法中,新增下面的一行程式碼:

var gravity = new b2Vec2(0,9.81);

這裡將介紹我們的第一個Box2D資料型別:b2Vec2。(注:在javascript中可沒這個資料型別,把它當成一個物件就好了

b2Vec2是一個2D的縱向量資料型別,它將儲存x和y向量分量。如你所見,構 造函式有兩個引數,都是數值,代表了x和y分量。通過這種方法我們定義 gravity變數作為一個向量,它有x=0(這意味著水平的重力)和y=-9.81(這意 味著近似的地球重力)。

物理學中說過,一個物體在地球表面自由下落的加速度近似為9.81m/s^2(米 每平方秒)也可寫作"m/s/s"。所以,假設沒有任何空氣阻力,我們在模擬一 個真實的世界(real-world)環境。解釋物體下落的原理已經超越本書的範 圍,但是你可以在Google或Wikipedia中搜尋"equations for a falling body"去獲得 更多的資訊。

 

2. 你可以設定你的遊戲為下面的這行程式碼:


var gravity = new b2Vec2(0,1.63);

你也可以將引數設定為(0,0)來模擬一個沒有重力的環境:

var gravity = new b2Vec2(0,0); 

3. 我們還需要告訴世界,當世界中的剛體靜止時,可以允許他們進入睡眠狀態,這樣它們將不受作用力的影響。

一個睡眠的剛體無需模擬,它只是表示自 己的存在,並靜止在它的位置上,不會對世界中的任何事物產生影響,允許 Box2D忽略它,而且因此會提升處理速度以及讓我們獲得更好的效能。

所以推 薦可能時讓剛體睡眠。 


4. 新增下面的一行,它只是一個簡單的布林(Boolean)變數定義:

var sleep = true; 


5. 最後,我們準備建立我們的第一個世界(world):


var world = new b2World(gravity,sleep); 


6.現在我們有一個容器來管理所有的剛體並且執行我們的動態模擬。

7.讓我們來簡單的回顧一下之前的程式碼,此刻,你的程式碼應該看起如下面 所示: 

<script type="text/javascript">
         function init(){
            var b2Vec2 = Box2D.Common.Math.b2Vec2
            ,b2AABB = Box2D.Collision.b2AABB
            ,b2BodyDef = Box2D.Dynamics.b2BodyDef
            ,b2Body = Box2D.Dynamics.b2Body
            ,b2FixtureDef = Box2D.Dynamics.b2FixtureDef
            ,b2Fixture = Box2D.Dynamics.b2Fixture
            ,b2World = Box2D.Dynamics.b2World
            ,b2MassData = Box2D.Collision.Shapes.b2MassData
            ,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
            ,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
            ,b2DebugDraw = Box2D.Dynamics.b2DebugDraw
            ,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
            ;
            
            var gravity = new b2Vec2(0, 9.81);
            var sleep = true;
            var world = new b2World(gravity, sleep);
            function main(){
               
            }

            main();
         }
         init();
      </script>

現在你學習了怎樣去建立並配置一個Box2D世界。讓我們來看看你將怎樣在它裡面實 現物理效果的模擬 。

 

 

 

執行模擬


你需要在每一幀都進行模擬,所以首先你需要一個監聽來觸發每一幀

1. 讓我們新增一點程式碼:

<script>
         function init(){
            var b2Vec2 = Box2D.Common.Math.b2Vec2
            ,b2AABB = Box2D.Collision.b2AABB
            ,b2BodyDef = Box2D.Dynamics.b2BodyDef
            ,b2Body = Box2D.Dynamics.b2Body
            ,b2FixtureDef = Box2D.Dynamics.b2FixtureDef
            ,b2Fixture = Box2D.Dynamics.b2Fixture
            ,b2World = Box2D.Dynamics.b2World
            ,b2MassData = Box2D.Collision.Shapes.b2MassData
            ,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
            ,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
            ,b2DebugDraw = Box2D.Dynamics.b2DebugDraw
            ,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
            ;

            var gravity = new b2Vec2(0, 9.81);
            var sleep = true;
            var world = new b2World(gravity, sleep);
            function main(){
               setInterval(updateWorld, 1000 / 60);
            }
            function updateWorld() {
               console.log("my awesome simulation runs here");
            }

            main();
         }
         init();
      </script>

 

沒有什麼新的,我們只是新增了一個setInterval迴圈定時執行,但是我們需要它有 序的執行updateWorld()方法中的模擬。

Box2D是通過模擬世界的離散時間步(step time)來進行模擬工作的。

這意味 著世界將在每一個時間步被更新。

這將取決於我們在模擬中所採用的時間 步。通常情況下,物理遊戲的時間步為1/60秒。

 

2. 下面是updateWorld函式的第一行: 

var timeStep  = 1/30 

只是定義了時間步還不夠。在每一步,每一個物理實體(physic entity)根據 作用於自身的作用力來更新(不包括睡眠狀態)。

處理這項任務的演算法叫約 束解算器(constraint solver)。

 

它是基於迴圈每一個約束然後解算來進的,一次一個,如果你想要學習更多的關於約束的知識,在google在搜 索"constraint algorithm"。

Where's the catch? 雖然單個約束可以被完美的解算,但是多個約束時,它 會攪亂之前已經解算的別的約束。

試想,當兩個球移動的時候:在真實的世界,每一個球的位置是在相同的時間更新。在電腦的模擬中,我們需要通過迴圈來更新球的位置,而每次只能一個。試想一下for迴圈每次遍歷更新一個球。只要球彼此間沒有發生 相互作用,一切正常執行,但是如何第二個球撞擊了第一個球,誰的位置被更新了?它們會重疊,這在剛體模擬中是不可能的事情。

通過取合適的近似值來解決這個問題,我們需要迴圈所有的約束不止一次。現在問題是:我們要迴圈多少次?

有兩種約束解算器:速率約束解算器(velocity constraint solver)和位置約束解 算器(position constraint solver)。速率約束解算器依據它們的在世界中的衝量 來移動物理實體。位置約束解算器調整物理實體的位置避免重疊。

所以,越高的便利次數,將會有更精確的模擬,但是效能會更低。我設法處 理超過100個物理實體使用10次速率和位置遍歷,雖然Box2D的作者推薦8次 速率和3次位置遍歷。

這將由你來決定使用的值。與此同時,我將使用對兩個約束解算器使用10次遍 歷。 
我們設定了兩個新變數: 
 

var velIterations:int=10;

var posIterations:int=10;

注:先不用理解太深,反正我也沒看懂,不管這個“約束”先 (=◎ω◎=)

最後我們準備呼叫world變數的step方法來更新模擬。 


在updateWorld方法中使用world,我們需要把world作為類變數宣告,如下所 示:



<script>
         function init(){
            var b2Vec2 = Box2D.Common.Math.b2Vec2
            ,b2AABB = Box2D.Collision.b2AABB
            ,b2BodyDef = Box2D.Dynamics.b2BodyDef
            ,b2Body = Box2D.Dynamics.b2Body
            ,b2FixtureDef = Box2D.Dynamics.b2FixtureDef
            ,b2Fixture = Box2D.Dynamics.b2Fixture
            ,b2World = Box2D.Dynamics.b2World
            ,b2MassData = Box2D.Collision.Shapes.b2MassData
            ,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
            ,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
            ,b2DebugDraw = Box2D.Dynamics.b2DebugDraw
            ,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
            ;

            
            var world;
            function main(){
               var gravity = new b2Vec2(0, 9.81);
               var sleep = true;
               world = new b2World(gravity, sleep);

               setInterval(updateWorld, 1000 / 60);
            }
            function updateWorld() {
               var timeStep = 1/30;
               var velIterations = 10;
               var posIterations = 10;
               world.Step(timeStep,velIterations,posIterations);
            }

            main();
         }
         init();
      </script>

現在我們有了我們自己的世界配置,然後執行。不幸的是,這是一個非常空

  洞的世界,它的裡面沒有任何東西。所以在下一章,我們將向世界中填充各

  種各樣的物理實體。

 

最後還有一件事情,在每一步之後,你需要清除作用力,讓模擬從 下一步再次開始。

你可以將這行world.ClearForces();程式碼新增到step方法之後;你最後的代 碼如下所示: 

<script>
         function init(){
            var b2Vec2 = Box2D.Common.Math.b2Vec2
            ,b2AABB = Box2D.Collision.b2AABB
            ,b2BodyDef = Box2D.Dynamics.b2BodyDef
            ,b2Body = Box2D.Dynamics.b2Body
            ,b2FixtureDef = Box2D.Dynamics.b2FixtureDef
            ,b2Fixture = Box2D.Dynamics.b2Fixture
            ,b2World = Box2D.Dynamics.b2World
            ,b2MassData = Box2D.Collision.Shapes.b2MassData
            ,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
            ,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
            ,b2DebugDraw = Box2D.Dynamics.b2DebugDraw
            ,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
            ;

            
            var world;
            function main(){
               var gravity = new b2Vec2(0, 9.81);
               var sleep = true;
               world = new b2World(gravity, sleep);

               setInterval(updateWorld, 1000 / 60);
            }
            function updateWorld() {
               var timeStep = 1/30;
               var velIterations = 10;
               var posIterations = 10;
               world.Step(timeStep,velIterations,posIterations);
               world.ClearForces(); // 清除作用力
            }

            main();
         }
         init();
</script>

 

 

原始碼全部程式碼在github上的demo1-1.html中,可檢視執行

概述

你剛剛學習了怎樣為在網頁中安裝使用Box2D。將它包含到你的專案中並執行,重力規則模 擬,管理時間步以及約束解算器。

到現在為止,你的網頁上其實並沒有顯示任何東西。

你有一個空的世界,準備成為你遊戲發生的容器。儲存它然後在每一個未來的專案中使用它!

 

 

 

 



注:轉載請註明出處部落格園:sheldon-二狗-偷飯貓(willian12345@126.com)

https://github.com/willian12345

 

相關文章