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