Box2D 物理引擎入门
轉自:https://www.jianshu.com/p/7681431618ec
一、什么是Box2D
Box2D是一個強大的開源物理游戲引擎,用來模擬2D剛體物體運動和碰撞,由Erin Catto早在2007年用C++語言開發。
Box2D集成了大量的物理力學和運動學的計算,并將物理模擬過程封裝到類對象中,將對物體的操作,以簡單友好的接口提供給開發者。我們只需要調用引擎中相應的對象或函數,就可以模擬現實生活中的加速、減速、拋物線運動、萬有引力、碰撞反彈等等各種真實的物理運動。(引用百度百科)
簡單的說,Box2D就是一個物理剛體仿真庫。
二、如何學習使用Box2D
Box2D是一個獨立的引擎框架,它的作用是幫助游戲開發者進行一些復雜的物理模擬運算,但是很多情況下它是作為某些游戲引擎的一個子模塊存在的。所以,我們可以借助一些游戲引擎來進行學習。對于iPhone、iPad和Android游戲,可以使用Cocos2d游戲引擎和Corona Framework。
關于原生Box2D的學習資料,網絡上面是多如牛毛,博客的話在CSDN和博客園上面都有。書籍方面也有諸如《Box2D物理游戲編程初學者指南》之類的東西。至于這些教程寫的到底好不好,這也只能仁者見仁,智者見智了。(反正我沒看)
我主要是基于bbframework框架開發游戲應用的時候使用到了這款出色的物理引擎,所以本文就在bbframework上進行介紹。
使用的工具:
- Sublime Text
- quick-x-player
關于bbframework(簡稱:bb),它是基于quick-cocos2d-x框架的一個再封裝框架,其核心應該可以說是cocos2d-Lua引擎(或者cocos2d-X)。而cocos2d其本身并不支持box2d物理引擎(cocos2d-JS除外),所以目前在lua上的box2d接口都是公司通過lua綁定將原生的C++接口綁定到lua上的。(難免有些API和C++原生不太一樣)
三、基本概念
Box2D物理引擎里面的所有類名都是以“b2”作為前綴的,以下是幾個比較重要的類。
1、世界(b2World)
物理世界只是一個抽象的概念,可以將其理解成是一個盒子,盒子里面放的是各種個樣的數學模型和物理模型(或者說就是N多的數學公式和物理公式),所有的物理模擬都在這個盒子內完成。
物理世界和cocos2d的渲染世界不同,渲染世界由場景、層和精靈等組成,在游戲運行時,渲染世界是可以看見(渲染顯示)、可以摸到(綁定觸摸事件)真實存在的。而物理世界的一切就跟萬物的靈魂一樣,看不見也摸不著,都是默默在后臺運行的一些數據片段。
萬物都是因為混沌初開,世界形成才存在的。同樣,要使用物理引擎里面的東西,一切也都要從創建世界開始。代碼如下:
-- 創建世界local world = b2World(b2Vec2(0, -9.8))-- 允許靜止的物體休眠world:SetAllowSleeping(true)-- 開啟連續物理檢測,使模擬更加的真實world:SetContinuousPhysics(true)創建世界可以通過調用b2World(gravity)函數進行創建,該函數的參數gravity是物理世界的重力加速度(g)。在物理學中,加速度是具有大小和方向的矢量,所以該參數可以使用二維向量來表示,其數據類型是b2Vec2,創建矢量可以直接調用b2Vec2(x, y)函數。
由于物理運算經常伴隨著平方、立方、開平方和開立方,甚至是更高次的冪運算,所以其計算量是非常大的,對于性能的消耗也是非??捎^。而游戲恰恰又非常強調運行的流暢性,所以很多時候當物體處于禁止狀態的時候并不需要實時進行物理運算,這時候就可以將其從物理模擬中暫時的剔除出去,以提高整體的計算效率。調用物理世界對象的SetAllowSleeping(isSleep)方法就可以設置世界內的物體是否在禁止的時候休眠,處于休眠狀態的物體將不參與物理運算。
同時,為了物理模擬的更加真實,通常還需要開啟物理世界的連續檢測。調用物理世界對象的SetContinuousPhysics(bool)方法便可以設置是否開啟連續檢測。(連續檢測會消耗一定的性能)
世界非常的大,可以說是無邊無際,然而游戲設備的屏幕是固定大小的,游戲的渲染畫面也就那么大,所以為了保證物理模擬的物體處于可見的畫面中,通常還需要給定一個邊緣,用于表示物理模擬的世界大小,所有的物體都添加到這個邊界里面。
物理世界里面的東西都可以看成是由剛體組成的,所以世界的邊界我們也可以創建一個四邊形剛體來表示,關于剛體的創建詳見下文。
2、剛體(b2Body)
首先,要知道什么是剛體?以《憤怒的小鳥》這款游戲為例,小鳥在離開彈弓之后的運行狀態完全是根據真實世界的物理效果進行變化的,那么在物理運算的時候,就需要一個剛體來表示小鳥(但不是小鳥本身),以參與物理運算。所以,剛體就是物理世界里面要進行物理模擬的物體。
在cocos2d中,你可以簡單的把剛體當成是一個數據對象,這個對象里面包含了各種各樣用于進行物理運算的數據(比如:質量、位置、旋轉角度等)。
那么,什么樣的東西適合在物理世界里面創建成剛體呢?物理世界簡單的可以包含氣體、液體和固體,Box2D是一個剛體仿真庫,對于氣體和液體的模擬并不是它的職責,所以它適合模擬的東西只剩下固體了,而且是那種在物理模擬中不會發生形變的固體(任何物體都會發生形變,這里只是一種理想狀態)。
由于剛體是現實世界物體的一個仿真模擬,所以剛體也必須包含一些現實物體的物理屬性,這些屬性可以簡單的稱之為對剛體的描述或者是定義。所以在創建剛體之前,需要先創建該剛體的剛體描述,用來描述剛體的物理屬性。
首先來看下Box2D原生對剛體描述的定義:
b2BodyDef() {// 用戶數據userData = NULL;// 剛體位置position.Set(0.0f, 0.0f);// 剛體角度angle = 0.0f;// 剛體線性速度linearVelocity.Set(0.0f, 0.0f);// 剛體角速度angularVelocity = 0.0f;// 剛體線性阻尼linearDamping = 0.0f;// 剛體角度阻尼angularDamping = 0.0f;// 剛體是否可以進行休眠allowSleep = true;// 剛體初始狀態是否處于喚醒狀態awake = true;// 剛體是否固定旋轉角度fixedRotation = false;// 剛體是否是一個快速移動的物體,為了防止發生擊穿想象,開啟它會增加處理時間bullet = false;// 剛體類型type = b2_staticBody;// 剛體是否處于活躍狀態active = true;// 剛體所受重力加速度影響的倍數gravityScale = 1.0f; }b2BodyDef是剛體描述的結構體類型,它可以包含以上14種物理信息。創建b2BodyDef的代碼如下:
local bodyDef = b2BodyDef()-- 類型:靜態(b2_staticBody),平臺(b2_kinematicBody),動態(b2_dynamicBody)bodyDef.type = b2_staticBodybodyDef.position = b2Vec2(0, 0)bodyDef.angle = math.rad(0)-- 用戶數據:存儲用戶的數據,可以是任何類型的數據。一般要求存儲的數據的類型是一致的bodyDef.userData = nil bodyDef.angularDamping = 0bodyDef.linearDamping = 0bodyDef.fixedRotation = false調用b2BodyDef()函數便可以創建一個剛體描述對象,然后我們可以隨意設置一些描述信息(不設置的時候,它們都有默認值)。
這里有注意的是:
1、剛體的類型:剛體類型分為靜態剛體、平臺剛體和動態剛體,對應值分別是:0、1和2。靜態剛體是不受力的作用而進行移動的,用于模擬地面、墻面等禁止的物體;平臺剛體可用于模擬游戲內的移動平臺等物體,這些物體和地面等幾乎一樣,但是可以進行位置移動等;動態剛體是最常見的,所有會動的物體都創建為動態剛體。
2、剛體的位置:在Box2D中可以使用b2Vec2類型的向量來表示坐標點。
3、剛體的角度:在Box2D中,剛體的角度是使用弧度制,并非和cocos2d一樣的角度制。
4、用戶數據:用戶數據用于保存一些程序員想要附加給剛體的信息,任何數據類型都可以,一般我們用于保存剛體對應的那個精靈節點對象(CCSprite)。
創建完剛體的描述,就可以通過描述對象告訴物理世界需要創建一個什么樣子的物體了。創建剛體的代碼如下:
-- 創建一個剛體對象,根據剛體定義創建 local body = world:CreateBody(bodyDef)通過調用物理世界對象的CreateBody(bodyDef)方法,物理世界就可以根據傳遞進去的bodyDef對象創建一個對應的剛體對象。
3、形狀(b2Shap)
創建好的剛體其實只是一個包含一些物理量的一個質點(有質量但是沒有大小的點),然而現實世界中的物體是有各種各樣的大小和形狀的,所以我們還需要為剛體創建對應的形狀。(物體的碰撞模擬也需要借助于形狀)
Box2D內置了以下幾種簡單形狀:
- 鏈條(b2ChainShape)
- 圓形(b2CircleShape)
- 邊線(b2EdgeShape)
- 多邊形(b2PolygonShape)
除了以上幾種之外,還可以借助PhysicsEditor等物理形狀編輯器進行描點來創建更加復雜的形狀。
在上文介紹世界的時候說到需要創建一個四邊形當成物理世界的邊界,那么這里可以選擇用四條邊首位相連,圍成一個四邊形。代碼如下:
local shape1 = b2EdgeShape() shape1:Set(b2Vec2(0 / 32, 0 / 32), b2Vec2(960 / 32, 0 / 32)) local shape2 = b2EdgeShape() shape2:Set(b2Vec2(0 / 32, 0 / 32), b2Vec2(0 / 32, 540 / 32)) local shape3 = b2EdgeShape() shape3:Set(b2Vec2(0 / 32, 540 / 32), b2Vec2(960 / 32, 540 / 32)) local shape4 = b2EdgeShape() shape4:Set(b2Vec2(960 / 32, 0 / 32), b2Vec2(960 / 32, 540 / 32))創建b2EdgeShape同樣可以通過調用b2EdgeShape()函數來實現,然后調用b2EdgeShape對象的Set(fromPoint, toPoint)方法來指定邊線的起點和終點。
這里我游戲的設計分辨率是 960 X 540 ,然后我創建的是一個和游戲設計分辨率同等尺寸的四邊形,但是可以看到起點和終點的x、y坐標值都被我除以了32,這是因為Box2D使用的度量是以“米”為單位,而cocos2d的坐標系是以像素為單位的,通常設置其轉換比例是1:32,也就是32像素的距離等價于Box2D中的1米,這樣的模擬效果是比較好的。
4、夾具(b2Fixture)
創建好形狀之后,需要將形狀和對應的剛體進行綁定,這樣剛體才能擁有形狀。b2Fixture類就是用于見形狀綁定到剛體上的,b2Fixture我們可以將其稱為“夾具”或者“材質”。
在創建b2Fixture之前,也需要先創建對應的材質描述對象(b2FixtureDef),設定一些材質信息。材質描述的定義如下:
b2FixtureDef() {// 形狀shape = NULL;// 用戶數據userData = NULL;// 摩擦系數friction = 0.2f;// 恢復系數restitution = 0.0f;// 密度density = 0.0f;// 是否為傳感器isSensor = false; }材質信息中的形狀和用戶數據請參考上文,這里就不在贅述了。重點看下以下幾個屬性:
- 摩擦系數:用于影響剛體的運動,取值通常在區間[0, 1],當然也可以更大。
- 恢復系數:或者稱之為“彈性系數”,用于剛體碰撞后能量的損失計算。取值通常在區間[0, 1],當然也可以更大。0表示發生非躺下碰撞,1表示發生完全彈性碰撞。
- 密度:密度通常用于計算剛體的質量,間接的影響剛體的慣性。
- 是否為傳感器:當設置isSensor為true時,剛體發生碰撞的時候并不會發生碰撞響應(反彈),但是會接收到碰撞的信號,所以該屬性可以理解為傳感器。
同樣,根據上面創建好的四條邊來創建四個材質定義對象,代碼如下:
-- 創建材質描述 local fixtureDef1 = b2FixtureDef() fixtureDef1.shape = shape1 local fixtureDef2 = b2FixtureDef() fixtureDef2.shape = shape2 local fixtureDef3 = b2FixtureDef() fixtureDef3.shape = shape3 local fixtureDef4 = b2FixtureDef() fixtureDef4.shape = shape4這里創建材質描述是調用b2FixtureDef()函數來實現,然后設置了描述對象的形狀信息,其它的信息全部使用默認的即可。
有了材質描述,接下來就可以創建對應的夾具(材質)了,代碼如下:
-- 創建四個夾具 body:CreateFixture(fixtureDef1) body:CreateFixture(fixtureDef2) body:CreateFixture(fixtureDef3) body:CreateFixture(fixtureDef4)創建夾具的方法是調用剛體的CreateFixture(b2FixtureDef)方法來實現的,并且夾具會見材質上的信息與該剛體進行綁定,一個剛體可以擁有多個夾具。
四、物理調試(Debug)
上文說過,物理世界的一切都是看不見的,但是有時候為了方便排錯,可以用其它的方法讓物理模擬變得可見。
比如:我們創建好了一個剛體,我們想要知道剛體對應到cocos2d渲染世界里面的位置,那么我們可以在cocos2d渲染世界里面創建一個lable標簽或者一個sprite精靈,并放到剛體的位置上面,這樣我們就等同于是讓剛體可見了。而對于邊線、圓之類的剛體形狀,我們可以使用一些游戲引擎的繪圖API在渲染世界內對應的進行繪制,這樣形狀也可以看到了。很多時候將這些數據進行可視化會幫助游戲開發者更好的進行物理排錯。
在bbframework中,可以使用以下代碼進行物理的可視化操作:
local debugDraw = GB2DebugDrawLayer:create(world, 32) self:add(debugDraw, 9999)GB2DebugDrawLayer這個類專門用于負責物理對象的可視化模擬,調用該類的create(b2World, PTM_RATIO)方法進行構造時,需要傳入物理世界對象和cocos2d與Box2D的度量單位比例(像素/米)。然后將GB2DebugDrawLayer的實例對象添加到當前場景的Layer上。
這樣我們便可以在渲染世界里面看到物理模擬的效果了。
物理模擬可視化
如上圖所示,我們可以看到屏幕的邊緣有紅色或者綠色的邊線,那就是上面創建的世界邊緣的四條邊。
總結
以上是生活随笔為你收集整理的Box2D 物理引擎入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 危机2.0时代,企业任重而道远
- 下一篇: 视频怎么分割片段?快速分割视频小技巧